Skip to content

[Compiler] Emit default destroyed events when resources are destroyed #4004

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 10, 2025

Conversation

SupunS
Copy link
Member

@SupunS SupunS commented Jun 6, 2025

Work towards #3993

Description

There are a few important points/behaviours that needed to be upheld by the implementation:

  • The default values of event-parameters are closure variables.
  • Event must be constructed before the enclosing resource is destroyed, but the actual emitting must happen after the resource is destroyed.
  • Default destroyed events can be inherited.

Implementation Approach

  • Generate a synthetic constructor for each ResourceDestroyed event, that assigns the parameters to fields.
    func init(a: T1, b: T2, ...) {
        self.a = a
        self.b = b
    }
  • For each resource that contains a ResourceDestroyed event, generate a getter-function, that return an array of events.
    resource R: I {
    
        var field: Int
    
        event ResourceDestroyed(a: T1 = v, b: T2 = self.field)
    
        func $ResourceDestroyed(): [AnyStruct] {
            return [
                R.ResourceDestroyed(a: v, b: self.field),    // event defined in the same type
                I.ResourceDestroyed(...),                    // inherited events
            ]
        }
    }
    This makes it seamless to capture the closure variables, that are used in argument default values.
  • During the resource destruction, call the $ResourceDestroyed() to get the event-values. Then once the resource is destroyed, emit the retrieved event-values (this part is already available in the value-implementation).

Alternatives Considered

  • Directly emit the event inside the generated $ResourceDestroyed function, rather than returning the event-values.

    resource R: I {
    
        event ResourceDestroyed(a: T1 = v1, b: T2 = v2)
    
        func $ResourceDestroyed() {
            emit R.ResourceDestroyed(a: v1, b: v2),    // event defined in the same type
            emit I.ResourceDestroyed(...),             // inherited events
        }
    }

    The problem is this will both construct and emit the event before the resource is destroyed.

  • Return the event's fields only by the $ResourceDestroyed function, rather than constructing an event value.

    • The problem is, to emit the event, it also needs the event's type. So we not only need to return the fields but also event type as a runtime type.
    • The returned value is an array-of-arrays, which is not an improvement from array-of-composites.

  • Targeted PR against master branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work
  • Code follows the standards mentioned here
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

@SupunS SupunS self-assigned this Jun 6, 2025
@SupunS SupunS added the Feature label Jun 6, 2025
Copy link

github-actions bot commented Jun 6, 2025

Cadence Benchstat comparison

This branch with compared with the base branch onflow:feature/compiler commit c99075e
The command for i in {1..N}; do go test ./... -run=XXX -bench=. -benchmem -shuffle=on; done was used.
Bench tests were run a total of 7 times on each branch.

Collapsed results for better readability

@SupunS SupunS marked this pull request as ready for review June 6, 2025 17:50
@SupunS SupunS requested review from turbolent and RZhang05 as code owners June 6, 2025 17:50
@turbolent
Copy link
Member

Great work! It makes sense to compile to a method, so self and fields are available 👍

In the examples, what do = v, = v1, and = v2 refer to?

@SupunS
Copy link
Member Author

SupunS commented Jun 10, 2025

v, v1, v2 all are any arbitrary value / expression (expressions that are supported by the default destroy event). In the example I mostly wanted to show that the default-value expression will be available/moved as-is inside the generated method.

Copy link
Member

@turbolent turbolent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! 👏

@SupunS SupunS merged commit 665d1cf into feature/compiler Jun 10, 2025
8 of 9 checks passed
@SupunS SupunS deleted the supun/destroy-event branch June 10, 2025 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants