|
| 1 | +// Copyright (C) 2026 Free Software Foundation, Inc. |
| 2 | + |
| 3 | +// This file is part of GCC. |
| 4 | + |
| 5 | +// GCC is free software; you can redistribute it and/or modify it under |
| 6 | +// the terms of the GNU General Public License as published by the Free |
| 7 | +// Software Foundation; either version 3, or (at your option) any later |
| 8 | +// version. |
| 9 | + |
| 10 | +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| 11 | +// WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 12 | +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 13 | +// for more details. |
| 14 | + |
| 15 | +// You should have received a copy of the GNU General Public License |
| 16 | +// along with GCC; see the file COPYING3. If not see |
| 17 | +// <http://www.gnu.org/licenses/>. |
| 18 | + |
| 19 | +#include "rust-drop-check.h" |
| 20 | +#include "rust-diagnostics.h" |
| 21 | +#include "rust-lang-item.h" |
| 22 | +#include "rust-hir-type-check.h" |
| 23 | +#include "optional.h" |
| 24 | +#include "rust-mapping-common.h" |
| 25 | +#include "rust-tyty.h" |
| 26 | + |
| 27 | +namespace Rust { |
| 28 | +namespace DropCheck { |
| 29 | + |
| 30 | +tl::optional<HirId> |
| 31 | +DropChecker::get_trait_hirid (LangItem::Kind lang_kind) |
| 32 | +{ |
| 33 | + auto &mappings = Analysis::Mappings::get (); |
| 34 | + auto opt_trait = mappings.lookup_lang_item_node (lang_kind); |
| 35 | + if (!opt_trait.has_value ()) |
| 36 | + return tl::nullopt; |
| 37 | + return mappings.lookup_node_to_hir (opt_trait.value ()); |
| 38 | +} |
| 39 | + |
| 40 | +tl::optional<HirId> |
| 41 | +DropChecker::get_drop_hirid () |
| 42 | +{ |
| 43 | + return get_trait_hirid (LangItem::Kind::DROP); |
| 44 | +} |
| 45 | + |
| 46 | +tl::optional<HirId> |
| 47 | +DropChecker::get_copy_hirid () |
| 48 | +{ |
| 49 | + return get_trait_hirid (LangItem::Kind::COPY); |
| 50 | +} |
| 51 | + |
| 52 | +bool |
| 53 | +DropChecker::is_manually_drop (const TyTy::BaseType *ty) |
| 54 | +{ |
| 55 | + auto &mappings = Analysis::Mappings::get (); |
| 56 | + auto manually_drop |
| 57 | + = mappings.lookup_lang_item (LangItem::Kind::MANUALLY_DROP); |
| 58 | + if (!manually_drop.has_value ()) |
| 59 | + return false; |
| 60 | + return ty->as<const TyTy::ADTType> ()->get_id () == manually_drop.value (); |
| 61 | +} |
| 62 | + |
| 63 | +tl::optional<HirId> |
| 64 | +DropChecker::impl_trait (const TyTy::BaseType *ty, HirId TraitHirId) |
| 65 | +{ |
| 66 | + TyTy::BaseType *orig = nullptr; |
| 67 | + auto tyctx = Resolver::TypeCheckContext::get (); |
| 68 | + if (!tyctx->lookup_type (ty->get_ref (), &orig)) |
| 69 | + return tl::nullopt; |
| 70 | + |
| 71 | + std::vector<std::pair<TyTy::BaseType *, HirId>> impl_map; |
| 72 | + if (!tyctx->peek_associated_impl_mapping_for_self (TraitHirId, &impl_map)) |
| 73 | + return tl::nullopt; |
| 74 | + |
| 75 | + if (orig->get_kind () != TyTy::TypeKind::ADT) |
| 76 | + return tl::nullopt; |
| 77 | + |
| 78 | + auto orig_adt = orig->as<TyTy::ADTType> (); |
| 79 | + for (auto &entry : impl_map) |
| 80 | + { |
| 81 | + auto RustType = entry.first; |
| 82 | + auto ImplBlockHirId = entry.second; |
| 83 | + |
| 84 | + if (RustType->get_kind () != TyTy::TypeKind::ADT) |
| 85 | + continue; |
| 86 | + |
| 87 | + if (orig_adt->get_id () == RustType->as<TyTy::ADTType> ()->get_id ()) |
| 88 | + return ImplBlockHirId; |
| 89 | + } |
| 90 | + return tl::nullopt; |
| 91 | +} |
| 92 | + |
| 93 | +tl::optional<HirId> |
| 94 | +DropChecker::impl_drop (const TyTy::BaseType *ty) |
| 95 | +{ |
| 96 | + auto opt_hirid = get_drop_hirid (); |
| 97 | + if (!opt_hirid.has_value ()) |
| 98 | + return tl::nullopt; |
| 99 | + return impl_trait (ty, opt_hirid.value ()); |
| 100 | +} |
| 101 | +tl::optional<HirId> |
| 102 | +DropChecker::impl_copy (const TyTy::BaseType *ty) |
| 103 | +{ |
| 104 | + auto opt_hirid = get_copy_hirid (); |
| 105 | + if (!opt_hirid.has_value ()) |
| 106 | + return tl::nullopt; |
| 107 | + return impl_trait (ty, opt_hirid.value ()); |
| 108 | +} |
| 109 | + |
| 110 | +bool |
| 111 | +DropChecker::needs_drop (const TyTy::BaseType *ty) |
| 112 | +{ |
| 113 | + // TODO : handle remaining types |
| 114 | + if (is_manually_drop (ty)) |
| 115 | + return false; |
| 116 | + |
| 117 | + if (impl_drop (ty).has_value ()) |
| 118 | + return true; |
| 119 | + |
| 120 | + switch (ty->get_kind ()) |
| 121 | + { |
| 122 | + case TyTy::ADT: |
| 123 | + { |
| 124 | + auto adt_type = ty->as<const TyTy::ADTType> (); |
| 125 | + for (auto variant : adt_type->get_variants ()) |
| 126 | + for (auto field : variant->get_fields ()) |
| 127 | + if (needs_drop (field->get_field_type ())) |
| 128 | + return true; |
| 129 | + return false; |
| 130 | + } |
| 131 | + case TyTy::TUPLE: |
| 132 | + { |
| 133 | + auto tuple_type = ty->as<const TyTy::TupleType> (); |
| 134 | + for (auto &field : tuple_type->get_fields ()) |
| 135 | + if (needs_drop (field.get_tyty ())) |
| 136 | + return true; |
| 137 | + return false; |
| 138 | + } |
| 139 | + case TyTy::ARRAY: |
| 140 | + return needs_drop (ty->as<const TyTy::ArrayType> ()->get_element_type ()); |
| 141 | + default: |
| 142 | + return false; |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +void |
| 147 | +DropChecker::check_copy_drop (const TyTy::BaseType *ty, HirId trait_hirid, |
| 148 | + location_t locus) |
| 149 | +{ |
| 150 | + bool is_e0184 = false; |
| 151 | + is_e0184 |= (trait_hirid == get_copy_hirid () && impl_drop (ty).has_value ()); |
| 152 | + is_e0184 |= (trait_hirid == get_drop_hirid () && impl_copy (ty).has_value ()); |
| 153 | + if (is_e0184) |
| 154 | + rust_error_at ( |
| 155 | + locus, ErrorCode::E0184, |
| 156 | + "the trait Copy cannot be implemented for type that has a destructor"); |
| 157 | +} |
| 158 | + |
| 159 | +void |
| 160 | +DropChecker::check_union (const TyTy::BaseType *ty) |
| 161 | +{ |
| 162 | + if (ty->get_kind () != TyTy::ADT) |
| 163 | + return; |
| 164 | + auto adt_type = ty->as<const TyTy::ADTType> (); |
| 165 | + if (adt_type->is_union ()) |
| 166 | + { |
| 167 | + for (auto variant : adt_type->get_variants ()) |
| 168 | + for (auto field : variant->get_fields ()) |
| 169 | + if (needs_drop (field->get_field_type ())) |
| 170 | + { |
| 171 | + rust_error_at (field->get_locus (), ErrorCode::E0740, |
| 172 | + "field must implement Copy or be wrapped in " |
| 173 | + "ManuallyDrop to be used in a union"); |
| 174 | + } |
| 175 | + } |
| 176 | +} |
| 177 | + |
| 178 | +void |
| 179 | +DropChecker::check_drop_call (HirId trait_hirid, location_t locus) |
| 180 | +{ |
| 181 | + if (trait_hirid == get_drop_hirid ()) |
| 182 | + { |
| 183 | + rust_error_at (locus, ErrorCode::E0040, |
| 184 | + "explicit use of destructor method"); |
| 185 | + } |
| 186 | +} |
| 187 | + |
| 188 | +} // namespace DropCheck |
| 189 | +} // namespace Rust |
0 commit comments