Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gcc/rust/Make-lang.in
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ GRS_OBJS = \
rust/rust-hir-generic-param.o \
rust/rust-type-util.o \
rust/rust-coercion.o \
rust/rust-drop-check.o \
rust/rust-casts.o \
rust/rust-unify.o \
rust/rust-hir-type-check-base.o \
Expand Down
189 changes: 189 additions & 0 deletions gcc/rust/typecheck/rust-drop-check.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// Copyright (C) 2026 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.

// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.

#include "rust-drop-check.h"
#include "rust-diagnostics.h"
#include "rust-lang-item.h"
#include "rust-hir-type-check.h"
#include "optional.h"
#include "rust-mapping-common.h"
#include "rust-tyty.h"

namespace Rust {
namespace DropCheck {

tl::optional<HirId>
DropChecker::get_trait_hirid (LangItem::Kind lang_kind)
{
auto &mappings = Analysis::Mappings::get ();
auto opt_trait = mappings.lookup_lang_item_node (lang_kind);
if (!opt_trait.has_value ())
return tl::nullopt;
return mappings.lookup_node_to_hir (opt_trait.value ());
}

tl::optional<HirId>
DropChecker::get_drop_hirid ()
{
return get_trait_hirid (LangItem::Kind::DROP);
}

tl::optional<HirId>
DropChecker::get_copy_hirid ()
{
return get_trait_hirid (LangItem::Kind::COPY);
}

bool
DropChecker::is_manually_drop (const TyTy::BaseType *ty)
{
auto &mappings = Analysis::Mappings::get ();
auto manually_drop
= mappings.lookup_lang_item (LangItem::Kind::MANUALLY_DROP);
if (!manually_drop.has_value ())
return false;
return ty->as<const TyTy::ADTType> ()->get_id () == manually_drop.value ();
}

tl::optional<HirId>
DropChecker::impl_trait (const TyTy::BaseType *ty, HirId TraitHirId)
{
TyTy::BaseType *orig = nullptr;
auto tyctx = Resolver::TypeCheckContext::get ();
if (!tyctx->lookup_type (ty->get_ref (), &orig))
return tl::nullopt;

std::vector<std::pair<TyTy::BaseType *, HirId>> impl_map;
if (!tyctx->peek_associated_impl_mapping_for_self (TraitHirId, &impl_map))
return tl::nullopt;

if (orig->get_kind () != TyTy::TypeKind::ADT)
return tl::nullopt;

auto orig_adt = orig->as<TyTy::ADTType> ();
for (auto &entry : impl_map)
{
auto RustType = entry.first;
auto ImplBlockHirId = entry.second;

if (RustType->get_kind () != TyTy::TypeKind::ADT)
continue;

if (orig_adt->get_id () == RustType->as<TyTy::ADTType> ()->get_id ())
return ImplBlockHirId;
}
return tl::nullopt;
}

tl::optional<HirId>
DropChecker::impl_drop (const TyTy::BaseType *ty)
{
auto opt_hirid = get_drop_hirid ();
if (!opt_hirid.has_value ())
return tl::nullopt;
return impl_trait (ty, opt_hirid.value ());
}
tl::optional<HirId>
DropChecker::impl_copy (const TyTy::BaseType *ty)
{
auto opt_hirid = get_copy_hirid ();
if (!opt_hirid.has_value ())
return tl::nullopt;
return impl_trait (ty, opt_hirid.value ());
}

bool
DropChecker::needs_drop (const TyTy::BaseType *ty)
{
// TODO : handle remaining types
if (is_manually_drop (ty))
return false;

if (impl_drop (ty).has_value ())
return true;

switch (ty->get_kind ())
{
case TyTy::ADT:
{
auto adt_type = ty->as<const TyTy::ADTType> ();
for (auto variant : adt_type->get_variants ())
for (auto field : variant->get_fields ())
if (needs_drop (field->get_field_type ()))
return true;
return false;
}
case TyTy::TUPLE:
{
auto tuple_type = ty->as<const TyTy::TupleType> ();
for (auto &field : tuple_type->get_fields ())
if (needs_drop (field.get_tyty ()))
return true;
return false;
}
case TyTy::ARRAY:
return needs_drop (ty->as<const TyTy::ArrayType> ()->get_element_type ());
default:
return false;
}
}

void
DropChecker::check_copy_drop (const TyTy::BaseType *ty, HirId trait_hirid,
location_t locus)
{
bool is_e0184 = false;
is_e0184 |= (trait_hirid == get_copy_hirid () && impl_drop (ty).has_value ());
is_e0184 |= (trait_hirid == get_drop_hirid () && impl_copy (ty).has_value ());
if (is_e0184)
rust_error_at (
locus, ErrorCode::E0184,
"the trait Copy cannot be implemented for type that has a destructor");
}

void
DropChecker::check_union (const TyTy::BaseType *ty)
{
if (ty->get_kind () != TyTy::ADT)
return;
auto adt_type = ty->as<const TyTy::ADTType> ();
if (adt_type->is_union ())
{
for (auto variant : adt_type->get_variants ())
for (auto field : variant->get_fields ())
if (needs_drop (field->get_field_type ()))
{
rust_error_at (field->get_locus (), ErrorCode::E0740,
"field must implement Copy or be wrapped in "
"ManuallyDrop to be used in a union");
}
}
}

void
DropChecker::check_drop_call (HirId trait_hirid, location_t locus)
{
if (trait_hirid == get_drop_hirid ())
{
rust_error_at (locus, ErrorCode::E0040,
"explicit use of destructor method");
}
}

} // namespace DropCheck
} // namespace Rust
57 changes: 57 additions & 0 deletions gcc/rust/typecheck/rust-drop-check.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (C) 2026 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.

// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.

#ifndef RUST_DROP_CHECK_H
#define RUST_DROP_CHECK_H

#include "optional.h"
#include "rust-mapping-common.h"
#include "rust-tyty.h"
#include "rust-hir-map.h"

namespace Rust {

class TypeCheckContext;

namespace DropCheck {

class DropChecker
{
public:
static tl::optional<HirId> get_trait_hirid (LangItem::Kind lang_kind);
static tl::optional<HirId> get_drop_hirid ();
static tl::optional<HirId> get_copy_hirid ();

static tl::optional<HirId> impl_trait (const TyTy::BaseType *ty,
HirId TraitHirId);
static tl::optional<HirId> impl_drop (const TyTy::BaseType *ty);
static tl::optional<HirId> impl_copy (const TyTy::BaseType *ty);

static bool needs_drop (const TyTy::BaseType *tyty);
static bool is_manually_drop (const TyTy::BaseType *ty);

static void check_copy_drop (const TyTy::BaseType *self, HirId trait_hirid,
location_t locus);
static void check_union (const TyTy::BaseType *ty);
static void check_drop_call (HirId trait_hirid, location_t locus);
};

} // namespace DropCheck
} // namespace Rust

#endif // RUST_DROP_CHECK_H
8 changes: 8 additions & 0 deletions gcc/rust/typecheck/rust-hir-type-check-expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "rust-tyty-util.h"
#include "rust-tyty.h"
#include "tree.h"
#include "rust-drop-check.h"

namespace Rust {
namespace Resolver {
Expand Down Expand Up @@ -1477,6 +1478,13 @@ TypeCheckExpr::visit (HIR::MethodCallExpr &expr)
infer_arguments.get_mut_regions ()
= fn->get_used_arguments ().get_regions ();
HIR::ImplBlock &impl = *resolved_candidate.item.impl.parent;
if (impl.has_trait_ref ())
{
HIR::TypePath &ref = impl.get_trait_ref ();
TraitReference *trait_reference = TraitResolver::Resolve (ref);
DropCheck::DropChecker::check_drop_call (
trait_reference->get_mappings ().get_hirid (), expr.get_locus ());
}
TyTy::BaseType *impl_self_infer
= TypeCheckItem::ResolveImplBlockSelfWithInference (impl,
expr.get_locus (),
Expand Down
7 changes: 7 additions & 0 deletions gcc/rust/typecheck/rust-hir-type-check-item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "optional.h"
#include "rust-canonical-path.h"
#include "rust-diagnostics.h"
#include "rust-drop-check.h"
#include "rust-hir-item.h"
#include "rust-hir-type-check-enumitem.h"
#include "rust-hir-type-check-implitem.h"
Expand Down Expand Up @@ -434,6 +435,8 @@ TypeCheckItem::visit (HIR::Union &union_decl)
context->insert_type (union_decl.get_mappings (), type);
infered = type;

DropCheck::DropChecker::check_union (type);

context->get_variance_analysis_ctx ().add_type_constraints (*type);
}

Expand Down Expand Up @@ -869,6 +872,10 @@ TypeCheckItem::validate_trait_impl_block (
context->insert_associated_impl_mapping (
trait_reference->get_mappings ().get_hirid (), self,
impl_block.get_mappings ().get_hirid ());

DropCheck::DropChecker::check_copy_drop (
self, trait_reference->get_mappings ().get_hirid (),
impl_block.get_trait_ref ().get_locus ());
}
}

Expand Down
2 changes: 2 additions & 0 deletions gcc/rust/typecheck/rust-hir-type-check.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ class TypeCheckContext
void insert_associated_impl_mapping (HirId trait_id,
TyTy::BaseType *impl_type,
HirId impl_id);
bool peek_associated_impl_mapping_for_self (
HirId trait_id, std::vector<std::pair<TyTy::BaseType *, HirId>> *mappings);
bool lookup_associated_impl_mapping_for_self (HirId trait_id,
TyTy::BaseType *self,
HirId *mapping);
Expand Down
14 changes: 14 additions & 0 deletions gcc/rust/typecheck/rust-typecheck-context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,20 @@ TypeCheckContext::insert_associated_impl_mapping (HirId trait_id,
associated_traits_to_impls[trait_id].emplace_back (impl_type, impl_id);
}

bool
TypeCheckContext::peek_associated_impl_mapping_for_self (
HirId trait_id, std::vector<std::pair<TyTy::BaseType *, HirId>> *mappings)
{
auto it = associated_traits_to_impls.find (trait_id);
if (it == associated_traits_to_impls.end ())
return false;

if (mappings != nullptr)
*mappings = it->second;

return true;
}

bool
TypeCheckContext::lookup_associated_impl_mapping_for_self (HirId trait_id,
TyTy::BaseType *self,
Expand Down
1 change: 1 addition & 0 deletions gcc/rust/util/rust-lang-item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ const BiMap<std::string, LangItem::Kind> Rust::LangItem::lang_items = {{
{"discriminant_kind", Kind::DISCRIMINANT_KIND},
{"discriminant_type", Kind::DISCRIMINANT_TYPE},
{"manually_drop", Kind::MANUALLY_DROP},
{"drop", Kind::DROP},
}};

tl::optional<LangItem::Kind>
Expand Down
1 change: 1 addition & 0 deletions gcc/rust/util/rust-lang-item.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class LangItem
DISCRIMINANT_KIND,

MANUALLY_DROP,
DROP,
};

static const BiMap<std::string, Kind> lang_items;
Expand Down
24 changes: 24 additions & 0 deletions gcc/testsuite/rust/compile/dropck-e0040-direct-drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![feature(no_core)]
#![feature(lang_items)]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
#[lang = "drop"]
pub trait Drop {
fn drop(&mut self);
}

struct Foo {
x: i32,
}

impl Drop for Foo {
fn drop(&mut self) {}
}

fn main() {
let mut x = Foo { x: -7 };
x.drop(); // { dg-error "explicit use of destructor method .E0040." }
}
Loading
Loading