diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 878613f7044..65c890e021d 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -225,6 +225,7 @@ GRS_OBJS = \ rust/rust-compile-struct-field-expr.o \ rust/rust-constexpr.o \ rust/rust-compile-base.o \ + rust/rust-compile-drop.o \ rust/rust-tree.o \ rust/rust-compile-context.o \ rust/rust-export-metadata.o \ diff --git a/gcc/rust/backend/rust-compile-base.cc b/gcc/rust/backend/rust-compile-base.cc index b7f2628f5d1..b938857900a 100644 --- a/gcc/rust/backend/rust-compile-base.cc +++ b/gcc/rust/backend/rust-compile-base.cc @@ -20,6 +20,7 @@ #include "rust-abi.h" #include "rust-compile-stmt.h" #include "rust-compile-expr.h" +#include "rust-compile-drop.h" #include "rust-compile-fnparam.h" #include "rust-compile-var-decl.h" #include "rust-compile-type.h" @@ -634,6 +635,8 @@ HIRCompileBase::compile_function_body (tree fndecl, return_value = coercion_site (id, return_value, actual, expected, lvalue_locus, rvalue_locus); + CompileDrop::emit_current_scope_drop_calls (ctx); + tree return_stmt = Backend::return_statement (fndecl, return_value, locus); ctx->add_statement (return_stmt); @@ -656,6 +659,7 @@ HIRCompileBase::compile_function_body (tree fndecl, // errors should have occurred location_t locus = function_body.get_locus (); tree return_value = unit_expression (locus); + CompileDrop::emit_current_scope_drop_calls (ctx); tree return_stmt = Backend::return_statement (fndecl, return_value, locus); ctx->add_statement (return_stmt); diff --git a/gcc/rust/backend/rust-compile-block.cc b/gcc/rust/backend/rust-compile-block.cc index 9a95d4e1578..245c16c52e8 100644 --- a/gcc/rust/backend/rust-compile-block.cc +++ b/gcc/rust/backend/rust-compile-block.cc @@ -17,8 +17,9 @@ // . #include "rust-compile-block.h" -#include "rust-compile-stmt.h" +#include "rust-compile-drop.h" #include "rust-compile-expr.h" +#include "rust-compile-stmt.h" #include "rust-hir-expr.h" namespace Rust { @@ -84,6 +85,7 @@ CompileBlock::visit (HIR::BlockExpr &expr) expr.get_locus ()); ctx->add_statement (assignment); } + CompileDrop::emit_current_scope_drop_calls (ctx); ctx->pop_block (); translated = new_block; diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h index 3fd686d81d1..ef24504023e 100644 --- a/gcc/rust/backend/rust-compile-context.h +++ b/gcc/rust/backend/rust-compile-context.h @@ -20,6 +20,7 @@ #define RUST_COMPILE_CONTEXT #include "rust-system.h" +#include "rust-compile-drop-candidate.h" #include "rust-hir-map.h" #include "rust-name-resolver.h" #include "rust-hir-type-check.h" @@ -101,6 +102,7 @@ class Context { scope_stack.push_back (scope); statements.push_back ({}); + block_drop_candidates.emplace_back (); } tree pop_block () @@ -111,6 +113,9 @@ class Context auto stmts = statements.back (); statements.pop_back (); + rust_assert (!block_drop_candidates.empty ()); + block_drop_candidates.pop_back (); + Backend::block_add_statements (block, stmts); return block; @@ -131,6 +136,18 @@ class Context void add_statement (tree stmt) { statements.back ().push_back (stmt); } + std::vector &peek_block_drop_candidates () + { + rust_assert (!block_drop_candidates.empty ()); + return block_drop_candidates.back (); + } + + void note_simple_drop_candidate (HirId hirid, location_t locus) + { + rust_assert (!block_drop_candidates.empty ()); + block_drop_candidates.back ().emplace_back (hirid, locus); + } + void insert_var_decl (HirId id, ::Bvariable *decl) { compiled_var_decls[id] = decl; @@ -419,6 +436,7 @@ class Context std::map compiled_labels; std::vector<::std::vector> statements; std::vector scope_stack; + std::vector<::std::vector> block_drop_candidates; std::vector<::Bvariable *> loop_value_stack; std::vector loop_begin_labels; std::map>> diff --git a/gcc/rust/backend/rust-compile-drop-candidate.h b/gcc/rust/backend/rust-compile-drop-candidate.h new file mode 100644 index 00000000000..12ce9b453d3 --- /dev/null +++ b/gcc/rust/backend/rust-compile-drop-candidate.h @@ -0,0 +1,40 @@ +// 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 +// . + +#ifndef RUST_COMPILE_DROP_CANDIDATE_H +#define RUST_COMPILE_DROP_CANDIDATE_H + +#include "rust-system.h" +#include "rust-hir-map.h" + +namespace Rust { +namespace Compile { + +struct DropCandidate +{ + DropCandidate (HirId hirid, location_t locus) : hirid (hirid), locus (locus) + {} + + HirId hirid; + location_t locus; +}; + +} // namespace Compile +} // namespace Rust + +#endif // RUST_COMPILE_DROP_CANDIDATE_H \ No newline at end of file diff --git a/gcc/rust/backend/rust-compile-drop.cc b/gcc/rust/backend/rust-compile-drop.cc new file mode 100644 index 00000000000..e15eb320dd6 --- /dev/null +++ b/gcc/rust/backend/rust-compile-drop.cc @@ -0,0 +1,113 @@ +// 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 +// . + +#include "rust-compile-drop.h" +#include "rust-compile-base.h" +#include "rust-compile-context.h" +#include "rust-compile-implitem.h" +#include "rust-hir-path-probe.h" +#include "rust-hir-trait-reference.h" +#include "rust-hir-type-bounds.h" +#include "rust-lang-item.h" +#include "rust-tyty.h" + +namespace Rust { +namespace Compile { + +bool +CompileDrop::type_has_drop_impl (Context *ctx, TyTy::BaseType *ty) +{ + auto drop_lang_item + = ctx->get_mappings ().lookup_lang_item (LangItem::Kind::DROP); + + if (!drop_lang_item.has_value ()) + return false; + + DefId drop_id = drop_lang_item.value (); + + auto candidates = Resolver::TypeBoundsProbe::Probe (ty); + for (auto &candidate : candidates) + { + Resolver::TraitReference *trait_ref = candidate.first; + if (trait_ref != nullptr && trait_ref->get_defid () == drop_id) + return true; + } + + return false; +} + +// Find the Drop trait, look for the drop method, and build the function call. +tree +CompileDrop::compile_drop_call (Context *ctx, Bvariable *var, + TyTy::BaseType *ty, location_t locus) +{ + auto drop_lang = ctx->get_mappings ().lookup_lang_item (LangItem::Kind::DROP); + if (!drop_lang.has_value ()) + return NULL_TREE; + + Resolver::TraitReference *drop_ref = nullptr; + bool ok + = ctx->get_tyctx ()->lookup_trait_reference (drop_lang.value (), &drop_ref); + if (!ok) + return NULL_TREE; + + HIR::PathIdentSegment segment ("drop"); + auto candidates + = Resolver::PathProbeImplTrait::Probe (ty->get_root (), segment, drop_ref); + + rust_assert (candidates.size () == 1); + + auto &candidate = *candidates.begin (); + rust_assert (candidate.is_impl_candidate ()); + rust_assert (candidate.ty->get_kind () == TyTy::TypeKind::FNDEF); + + auto *fn_type = static_cast (candidate.ty); + tree fn_addr + = CompileInherentImplItem::Compile (candidate.item.impl.impl_item, ctx, + fn_type, locus); + + tree var_expr = Backend::var_expression (var, locus); + tree var_addr = HIRCompileBase::address_expression (var_expr, locus); + + return Backend::call_expression (fn_addr, {var_addr}, nullptr, locus); +} + +void +CompileDrop::emit_current_scope_drop_calls (Context *ctx) +{ + auto &drop_candidates = ctx->peek_block_drop_candidates (); + + for (auto it = drop_candidates.rbegin (); it != drop_candidates.rend (); ++it) + { + TyTy::BaseType *ty = nullptr; + Bvariable *var = nullptr; + + bool ok = ctx->get_tyctx ()->lookup_type (it->hirid, &ty); + rust_assert (ok); + + ok = ctx->lookup_var_decl (it->hirid, &var); + rust_assert (ok); + + tree drop_call = CompileDrop::compile_drop_call (ctx, var, ty, it->locus); + if (drop_call != NULL_TREE) + ctx->add_statement (convert_to_void (drop_call, ICV_STATEMENT)); + } +} + +} // namespace Compile +} // namespace Rust diff --git a/gcc/rust/backend/rust-compile-drop.h b/gcc/rust/backend/rust-compile-drop.h new file mode 100644 index 00000000000..2a710d6ceb9 --- /dev/null +++ b/gcc/rust/backend/rust-compile-drop.h @@ -0,0 +1,41 @@ +// 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 +// . + +#ifndef RUST_COMPILE_DROP_H +#define RUST_COMPILE_DROP_H + +#include "rust-compile-context.h" + +namespace Rust { +namespace Compile { + +class CompileDrop +{ +public: + static bool type_has_drop_impl (Context *ctx, TyTy::BaseType *ty); + + static tree compile_drop_call (Context *ctx, Bvariable *var, + TyTy::BaseType *ty, location_t locus); + + static void emit_current_scope_drop_calls (Context *ctx); +}; + +} // namespace Compile +} // namespace Rust + +#endif // RUST_COMPILE_DROP_H \ No newline at end of file diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc index d57616e6d09..2179916d5c1 100644 --- a/gcc/rust/backend/rust-compile-pattern.cc +++ b/gcc/rust/backend/rust-compile-pattern.cc @@ -17,14 +17,18 @@ // . #include "rust-compile-pattern.h" +#include "print-tree.h" +#include "rust-compile-drop.h" #include "rust-compile-expr.h" #include "rust-compile-resolve-path.h" -#include "rust-constexpr.h" #include "rust-compile-type.h" -#include "print-tree.h" +#include "rust-constexpr.h" #include "rust-diagnostics.h" #include "rust-hir-pattern-abstract.h" #include "rust-hir-pattern.h" +#include "rust-hir-trait-reference.h" +#include "rust-hir-type-bounds.h" +#include "rust-lang-item.h" #include "rust-system.h" #include "rust-tyty.h" #include "tree.h" @@ -1343,6 +1347,24 @@ CompilePatternLet::visit (HIR::IdentifierPattern &pattern) auto s = Backend::init_statement (fnctx.fndecl, var, init_expr); ctx->add_statement (s); } + + TyTy::BaseType *drop_ty = ty; + if (pattern.get_is_ref ()) + { + auto ref_ty = ty->try_as (); + rust_assert (ref_ty != nullptr); + drop_ty = ref_ty->get_base (); + } + + if (!CompileDrop::type_has_drop_impl (ctx, drop_ty)) + return; + + if (!pattern.has_subpattern () && !pattern.get_is_ref ()) + ctx->note_simple_drop_candidate (pattern.get_mappings ().get_hirid (), + pattern.get_locus ()); + else + rust_sorry_at (pattern.get_locus (), + "drop trait not supported for subpatterns and ref patterns"); } void diff --git a/gcc/testsuite/rust/compile/drop-ref-pattern.rs b/gcc/testsuite/rust/compile/drop-ref-pattern.rs new file mode 100644 index 00000000000..f0053dcd275 --- /dev/null +++ b/gcc/testsuite/rust/compile/drop-ref-pattern.rs @@ -0,0 +1,23 @@ +#![feature(no_core)] +#![feature(lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "drop"] +pub trait Drop { + fn drop (&mut self); +} + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) {} +} + +fn main() { + { + let ref _x = Droppable; // { dg-message "sorry, unimplemented: drop trait not supported for subpatterns and ref patterns" } + } +} \ No newline at end of file diff --git a/gcc/testsuite/rust/execute/drop-block-scope.rs b/gcc/testsuite/rust/execute/drop-block-scope.rs new file mode 100644 index 00000000000..943cf5ee1e5 --- /dev/null +++ b/gcc/testsuite/rust/execute/drop-block-scope.rs @@ -0,0 +1,41 @@ +// { dg-output "d\r*\nd\r*\nd\r*\n" } +// { dg-additional-options "-w" } +#![feature(no_core)] +#![feature(lang_items)] +#![no_core] + +extern "C" { + fn printf(s: *const i8, ...); +} + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "drop"] +pub trait Drop { + fn drop(&mut self); +} + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) { + let msg = "d\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +fn main() -> i32 { + { + let _x = Droppable; + } + { + let x = Droppable; + } + { + let mut x = Droppable; + } + 0 +} diff --git a/gcc/testsuite/rust/execute/drop-function-scope-unit.rs b/gcc/testsuite/rust/execute/drop-function-scope-unit.rs new file mode 100644 index 00000000000..e4dd91f985a --- /dev/null +++ b/gcc/testsuite/rust/execute/drop-function-scope-unit.rs @@ -0,0 +1,37 @@ +// { dg-output "d\r*\n" } +// { dg-additional-options "-w" } +#![feature(no_core)] +#![feature(lang_items)] +#![no_core] + +extern "C" { + fn printf(s: *const i8, ...); +} + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "drop"] +pub trait Drop { + fn drop(&mut self); +} + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) { + let msg = "d\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +fn f() { + let _x = Droppable; +} + +fn main() -> i32 { + f(); + 0 +} diff --git a/gcc/testsuite/rust/execute/drop-function-scope.rs b/gcc/testsuite/rust/execute/drop-function-scope.rs new file mode 100644 index 00000000000..70344865dd0 --- /dev/null +++ b/gcc/testsuite/rust/execute/drop-function-scope.rs @@ -0,0 +1,33 @@ +// { dg-output "d\r*\n" } +// { dg-additional-options "-w" } +#![feature(no_core)] +#![feature(lang_items)] +#![no_core] + +extern "C" { + fn printf(s: *const i8, ...); +} + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "drop"] +pub trait Drop { + fn drop(&mut self); +} + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) { + let msg = "d\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +fn main() -> i32 { + let _x = Droppable; + 0 +}