gccrs: Add Drop support for block-local variable#4564
Conversation
e21459d to
dab0a99
Compare
| auto s = Backend::init_statement (fnctx.fndecl, var, init_expr); | ||
| ctx->add_statement (s); | ||
| } | ||
|
|
There was a problem hiding this comment.
For now I only handle the simple case like:
- let x = Droppable;
- let mut x = Droppable;
- let _x = Droppable;
I skipped more complex patterns for this first version, like ref and subpatterns.
I wanted to start with the smallest case first and then extend it later.
Does this scope look okay for a first draft?
There was a problem hiding this comment.
Then you might want to add a placeholder error message for now:
else if (type_has_drop_impl (ctx, ty))
rust_sorry_at (pattern.get_locus (), "drop trait not supported for subpatterns & ref patterns");There was a problem hiding this comment.
Thank you for pointing this out. I’ll add a rust_sorry_at for Drop types with ref bindings and subpatterns.
| return block_drop_candidates.back (); | ||
| } | ||
|
|
||
| void note_simple_drop_candidate (HirId hirid, location_t locus) |
There was a problem hiding this comment.
I am storing the drop candidates in Context for now.
The idea is that when we encounter an eligible variable in let, I store it in the array.
Please let me know if this is an ideal place.
There was a problem hiding this comment.
I think it's fine for now, but we should think about what future APIs will look like and start building towards that in a later PR
There was a problem hiding this comment.
I'll keep the current approach for now and think about what future APIs should look like in a follow-up PR.
Thank you!
| namespace Compile { | ||
|
|
||
| static tree | ||
| compile_drop_call (Context *ctx, Bvariable *var, TyTy::BaseType *ty, |
There was a problem hiding this comment.
This helper finds the Drop trait, looks for the drop method, and builds the function call.
I put it in rust-compile-block.cc for now because it is only used when leaving a block.
I think this may need to move to a separate file later, since other scenarios may also need to use it.
There was a problem hiding this comment.
This helper finds the Drop trait, looks for the drop method, and builds the function call.
If you felt you had to write this in a github comment writing it as a regular code comment may make sense
There was a problem hiding this comment.
Thanks, that makes sense. I'll move that explanation into a comment above compile_drop_call.
| namespace Compile { | ||
|
|
||
| static bool | ||
| type_has_drop_impl (Context *ctx, TyTy::BaseType *ty) |
There was a problem hiding this comment.
I added this helper to avoid getting variables whose type does not implement Drop.
There was a problem hiding this comment.
Likewise I think this function would be great in a file dedicated to Drop functions
There was a problem hiding this comment.
Done, I moved it to CompileDrop in rust-compile-drop.h and rust-compile-drop.cc.
Thank you!
Polygonalr
left a comment
There was a problem hiding this comment.
Arthur or Pierre-Emmanuel will do the proper review, but minor stuff from me.
| auto s = Backend::init_statement (fnctx.fndecl, var, init_expr); | ||
| ctx->add_statement (s); | ||
| } | ||
|
|
There was a problem hiding this comment.
Then you might want to add a placeholder error message for now:
else if (type_has_drop_impl (ctx, ty))
rust_sorry_at (pattern.get_locus (), "drop trait not supported for subpatterns & ref patterns");| pub trait Sized {} | ||
|
|
||
| #[lang = "drop"] | ||
| pub trait Drop { |
There was a problem hiding this comment.
In the future, I think this should be implemented as part of the compiler. You may want to take a look at how other traits (e.g. Copy, Eq) are implemented under gcc/rust/expand/rust-derive.cc.
There was a problem hiding this comment.
Thank you, I’ll take a look at rust-derive.cc. For this draft, I kept the no_core test self-contained, but I agree the Drop support should be handled more directly in the compiler in the future.
| namespace Compile { | ||
|
|
||
| static tree | ||
| compile_drop_call (Context *ctx, Bvariable *var, TyTy::BaseType *ty, |
There was a problem hiding this comment.
This helper finds the Drop trait, looks for the drop method, and builds the function call.
If you felt you had to write this in a github comment writing it as a regular code comment may make sense
| void note_simple_drop_candidate (HirId hirid, location_t locus) | ||
| { | ||
| rust_assert (!block_drop_candidates.empty ()); | ||
| block_drop_candidates.back ().push_back ({hirid, locus}); |
There was a problem hiding this comment.
Couldn't you emplace_back here since you're building your DropCandidate?
There was a problem hiding this comment.
Yes, good point. I'll add a small DropCandidate constructor and use emplace_back here.
CohenArthur
left a comment
There was a problem hiding this comment.
I think this looks really good as a first approach and the code is clear. Well done
| struct DropCandidate | ||
| { | ||
| HirId hirid; | ||
| location_t locus; | ||
| }; | ||
|
|
There was a problem hiding this comment.
I would like to see separate files for these since they are exclusively drop-related. I think it's a great idea to create a separate struct, but it will eventually become more complicated and more methods/functions will be added, so it makes sense to create its own file now
There was a problem hiding this comment.
Done, I moved DropCandidate to rust-compile-drop.h together with the other Drop-related helpers.
Thank you!
| namespace Compile { | ||
|
|
||
| static tree | ||
| compile_drop_call (Context *ctx, Bvariable *var, TyTy::BaseType *ty, |
There was a problem hiding this comment.
Likewise this is a great function but I would put it in a class in a separate file. It would also help with caching down the line if that's something we eventually want to do
There was a problem hiding this comment.
Done, I also moved it into the CompileDrop in rust-compile-drop.h and rust-compile-drop.cc, which should make it easier to extend later.
Thanks!
| return block_drop_candidates.back (); | ||
| } | ||
|
|
||
| void note_simple_drop_candidate (HirId hirid, location_t locus) |
There was a problem hiding this comment.
I think it's fine for now, but we should think about what future APIs will look like and start building towards that in a later PR
| namespace Compile { | ||
|
|
||
| static bool | ||
| type_has_drop_impl (Context *ctx, TyTy::BaseType *ty) |
There was a problem hiding this comment.
Likewise I think this function would be great in a file dedicated to Drop functions
fe7fb82 to
6ac7327
Compare
| { | ||
| DropCandidate (HirId hirid, location_t locus) : hirid (hirid), locus (locus) | ||
| {} | ||
| #include "rust-compile-context.h" |
There was a problem hiding this comment.
I reworked this to avoid the forward declarations. rust-compile-drop.h now includes the required context header directly.
DropCandidate now lives in rust-compile-drop-candidate.h, so it remains in a drop-related file, while rust-compile-context.h only includes that small state type instead of the full rust-compile-drop.h header.
Does this look good to you?
CohenArthur
left a comment
There was a problem hiding this comment.
One small comment but the implementation looks really good to me, well done Janet
| for (auto &candidate : candidates) | ||
| { | ||
| if (!candidate.is_impl_candidate () | ||
| || candidate.ty->get_kind () != TyTy::TypeKind::FNDEF) | ||
| continue; | ||
|
|
||
| auto *fn_type = static_cast<TyTy::FnType *> (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); | ||
| } | ||
| return NULL_TREE; |
There was a problem hiding this comment.
Here we should only ever have one candidate which corresponds to the actual Drop lang item trait and its drop function, otherwise we are doing something really evil and the compiler should crash.
| for (auto &candidate : candidates) | |
| { | |
| if (!candidate.is_impl_candidate () | |
| || candidate.ty->get_kind () != TyTy::TypeKind::FNDEF) | |
| continue; | |
| auto *fn_type = static_cast<TyTy::FnType *> (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); | |
| } | |
| return NULL_TREE; | |
| 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<TyTy::FnType *> (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); | |
There was a problem hiding this comment.
Thank you, I understand now. Since this should resolve to exactly one Drop candidate, I changed it to use assertions so invalid compiler state is caught instead of being ignored.
| 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); |
There was a problem hiding this comment.
Since we are always passing the Context *ctx argument in each of these functions, I think it would be worth in a later PR to keep it as a class member and reuse it. But don't do it now as this is fine for now 👍
There was a problem hiding this comment.
Thanks, that makes sense. I’ll keep the current approach for now and consider this in a follow-up cleanup PR.
Add the basic structure for saving local variables that may need Drop. gcc/rust/ChangeLog: * backend/rust-compile-context.h (struct DropCandidate): New struct for tracking block-local drop candidates. Signed-off-by: lishin <lishin1008@gmail.com>
Add a helper for building Drop calls, and use it when leaving a block. Add a block-exit test case. gcc/rust/ChangeLog: * backend/rust-compile-block.cc (compile_drop_call): New helper to build a drop call. (CompileBlock::visit): Add drop calls for candidates. * backend/rust-compile-pattern.cc (type_has_drop_impl): New helper to check whether a type implements Drop. (CompilePatternLet::visit): Save initialized simple drop candidates. gcc/testsuite/ChangeLog: * rust/execute/drop-block-scope.rs: New test. Signed-off-by: lishin <lishin1008@gmail.com>
Add an unsupported error for ref patterns and subpatterns. Add an unsupported ref-pattern test case, and extend the existing block-exit test case. gcc/rust/ChangeLog: * backend/rust-compile-pattern.cc (CompilePatternLet::visit): Check ref pattern base types and emit a sorry for unsupported Drop ref/subpatterns. gcc/testsuite/ChangeLog: * rust/execute/drop-block-scope.rs: Extend test coverage for simple immutable and mutable bindings. * rust/compile/drop-ref-pattern.rs: New test. Signed-off-by: lishin <lishin1008@gmail.com>
Refactor helper code into separate rust-compile-drop files. This keeps the Drop-related logic in one place. gcc/rust/ChangeLog: * Make-lang.in: Add rust-compile-drop.o. * backend/rust-compile-block.cc (compile_drop_call): Move to CompileDrop. (CompileBlock::visit): Use CompileDrop to emit scope drop calls. * backend/rust-compile-context.h (struct DropCandidate): Move to rust-compile-drop.h. * backend/rust-compile-pattern.cc (type_has_drop_impl): Move to CompileDrop. (CompilePatternLet::visit): Use CompileDrop to check Drop impls. * backend/rust-compile-drop.cc: New file. * backend/rust-compile-drop.h: New file. Signed-off-by: Lishin <lishin1008@gmail.com>
Add function-scope Drop support for the implicit return case. Add two test cases. This does not yet support explicit return. gcc/rust/ChangeLog: * backend/rust-compile-base.cc (HIRCompileBase::compile_function_body): Add drop calls at the end of function bodies. gcc/testsuite/ChangeLog: * rust/execute/drop-function-scope-unit.rs: New test. * rust/execute/drop-function-scope.rs: New test. Signed-off-by: lishin <lishin1008@gmail.com>
Move DropCandidate into a small drop-specific header and fix the include sturcture. Also add missing copyright headers, fix the include guard name, and assert that Drop lookup returns a single function candidate. gcc/rust/ChangeLog: * backend/rust-compile-context.h: Include rust-compile-drop-candidate.h instead of rust-compile-drop.h. * backend/rust-compile-drop.cc: Add copyright header. (CompileDrop::compile_drop_call): Assert that Drop lookup returns a single function candidate. * backend/rust-compile-drop.h: Add copyright header. Include rust-compile-context.h. (RUST_COMPILE_DROP): Rename to... (RUST_COMPILE_DROP_H): ...this. (DropCandidate): Move to... * backend/rust-compile-drop-candidate.h: ...this new file. Signed-off-by: Lishin <lishin1008@gmail.com>
fe46d6c to
d35c6aa
Compare
CohenArthur
left a comment
There was a problem hiding this comment.
looks good to me! well done!!
This PR consists of 4 commits:
For now, it handles cases like:
It also emits a temporary rust_sorry_at for unsupported Drop ref/subpatterns.
commit 1
gccrs: add block drop candidate tracking infrastructure
commit 2
gccrs: add block-exit Drop calls
commit 3
gccrs: report unsupported Drop ref patterns
commit 4
gccrs: move Drop helpers to separate files
gcc/rust/ChangeLog:
Tested with:
make all-gcc make check-rust RUNTESTFLAGS="--all compile.exp=drop-ref-pattern.rs" make check-rust RUNTESTFLAGS="--all compile.exp=drop-lang-item.rs" make check-rust RUNTESTFLAGS="--all execute.exp=drop-block-scope.rs"