gccrs-final-report
Project Overview
A short summary of Rust Drop, the work completed in gccrs, and future work.
- Project: Add infrastructure to handle the
Droptrait - Organization: GNU Compiler Collection (GCC)
- Mentors: Arthur Cohen and Pierre-Emmanuel Patry
During Google Summer of Code 2026, I worked on adding the basic infrastructure for Rust’s Drop trait to gccrs. The work involved C++, Rust, GCC tree expressions, BIR, control-flow graphs, and DejaGNU tests.
1. What is Drop?
Rust uses the Drop trait for value cleanup. The cleanup runs after a value is no longer needed.
fn example() {
let a = Droppable("a");
{
let b = Droppable("b");
}
}
In this example, the inner block ends first, so b is dropped first. Later, a is dropped at the end of the function. Values in the same scope are dropped in reverse declaration order. This is called LIFO order.
Early exits also need cleanup. Examples include return, break, and continue. A move transfers a value to a new location. The compiler must not drop the old location. The programmer writes the cleanup code in Drop::drop. The compiler inserts the call at the correct scope exit. It also skips the call for a moved value.
2. Support before and after the project
Before this project, gccrs did not automatically insert Drop calls at scope exit.
The original project plan started with a small and testable subset: simple, non-generic local variables. The project later added multiple locals, correct LIFO order, nested scopes, and whole-local moves. Temporaries, partial initialization, and generic cases were not completed and remain future work.
| Feature | Before this project | What works now | Related pull requests |
|---|---|---|---|
Recognizing the Drop trait | gccrs did not know that Drop was a special Rust trait. | Merged: gccrs recognizes Drop as a language item. | #4559 |
| Dropping locals and parameters | Local variables and function parameters were not dropped automatically when their scope ended. | Merged: gccrs can Drop supported simple local bindings and function parameters in block and function scopes. | #4564, #4591 |
| Drop order | gccrs did not handle multiple values or nested scopes. | Merged: the newest value is dropped first. Inner scopes are cleaned before outer scopes, and body locals are dropped before function parameters. | #4632, #4718 |
| Returning or leaving a block | Normal function exits, return, break, and continue did not run cleanup. | Merged: normal exits, tail expressions, explicit return, and the tested unlabeled break and continue cases run cleanup. | #4602, #4621, #4685, #4711, #4710 |
| Straight-line moves | gccrs could still Drop the old location after its value was moved. | Merged in the BIR/borrow-check path: gccrs can detect a whole-local move in straight-line code and skip Drop at the old location. | #4730, #4748 |
| Conditional moves | gccrs could not tell that a value might be moved in one branch but not another. | Under review: CFG analysis tracks the value across branches, and backend Drop flags handle the runtime decision. Neither PR is merged yet. | #4777, #4798 |
This is a useful subset of Drop behavior, but it is not full Rust Drop support. The Pull Request Notes show the exact implementation and tests behind each item.
3. Future work
3.1 Complete labeled break and continue cleanup
The current cleanup handles the tested unlabeled break and continue cases. One loop-body case can still run Drop twice when a break appears before a later local declaration. This is tracked in issue #4717.
Labeled break and continue are not supported yet. A labeled jump may leave several nested scopes. gccrs must clean every scope between the jump and its target.
3.2 Support partial moves
A move can take one field but leave the other field in place:
struct Pair {
left: Droppable,
right: Droppable,
}
let pair = Pair {
left: Droppable("left"),
right: Droppable("right"),
};
let a = pair.left;
pair.left was moved, but pair.right still needs cleanup. gccrs must track the fields separately. The same tracking is needed when only some fields have been initialized.
3.3 Support temporary values
A value can need cleanup even when it is not stored in a named local variable:
fn f() {
make_droppable();
}
The result of make_droppable() is a temporary value. gccrs must Drop it at the end of its temporary scope.
3.4 Support generic Drop
The same generic function can receive types with different cleanup needs:
fn finish<T>(value: T) {
// value is dropped when the function ends, if needed
}
fn main() {
finish(42); // i32 does not need Drop
finish(Droppable("x")); // Droppable must run Drop
}
gccrs must generate the correct cleanup for each concrete T. This still needs design and implementation work.
4. Reflection and Thanks
This project taught me about GCC tree expressions, structured cleanup, the boundary between the gccrs frontend and backend, BIR, CFG data-flow analysis, and compiler testing.
I wanted to work on generic Drop support. I did not reach this part during GSoC. BIR and CFG analysis took more time than I expected. I also spent time making the gccrs scope design closer to the rustc design. Both tasks were necessary for correct move handling. I chose to finish them first.
I would like to thank my mentors, Arthur Cohen and Pierre-Emmanuel Patry, for their reviews, explanations, and design feedback throughout the project.
Read the Design Note → · Browse the Pull Request Notes → · View the Weekly Updates →