-
Notifications
You must be signed in to change notification settings - Fork 139
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
[Compiler] Support inherited functions and conditions through delegation #3734
Conversation
269e3d6
to
2a13f25
Compare
…into supun/generate-code-for-interfaces
Cadence Benchstat comparisonThis branch with compared with the base branch onflow:feature/compiler commit 3a904fd Collapsed results for better readability
|
aed29ca
to
5ded59f
Compare
…into supun/generate-code-for-interfaces
5c1c4c0
to
47355ef
Compare
47355ef
to
b0570bb
Compare
Out of curiosity. Why not like this ? struct interface A {
access(all) fun test(_ a: Int): Int {}
// condition gets extracted out as a function
access(all) view fun $A.test.preConditions(_ a: Int): Void {
if !(a > 10) {
panic("a must be larger than 10")
}
return
}
}
struct interface B: A {
access(all) fun test(_ a: Int): Int {}
access(all) view fun $B.test.preConditions(_ a: Int): Void {
parent.$A.test.preConditions(a)
}
}
struct C: B {
fun test(_ a: Int): Int {
parent.$B.test.preConditions(a) // calls into the inherited pre-condition function (a static function invocation)
return a + 3
}
} |
@bluesign That's a good question! I initially thought about it too, and It's possible to do as what you've suggested. However, if there are multiple interface conformances for The way it works now is, all those different paths gets flattened at compile time (the same way the checker does), and only one path would be preserved. I need to document these somewhere. |
@SupunS Does that mean that if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! 👏
I like this separate compilation approach a lot more compared to the copying of code on source-level.
As for the desugaring: It currently requires quite a lot of code to implement a transformation, I wonder if we can use a form of "template", where we parse Cadence source code and then have a function produce a copy of the AST of it, where a particular node in the tree is replaced with a parameter value.
For example, something like (pseudo-code):
type TemplateFunc func(args map[string]ast.Expression) []ast.Statements
var conditionTemplate = newTemplate(
`if (!$test) { panic($message) }`,
"$test",
"$message",
)
func newTemplate(source string, params ...string) TemplateFunc {
return func(args map[string]ast.Expression) []ast.Statements {
// parse source to AST
// replace identifiers which have $ prefix with args
// return replaced AST
}
}
Yes, correct. For example, for a complex inheritance chain like: cadence/bbq/vm/test/vm_test.go Lines 2590 to 2628 in b0570bb
The eventual concrete type's function would look like below (this is the actual desugared-tree pretty-printed): struct A: B {
access(all)
fun test() {
self.$B.test.preConditions()
self.$C.test.preConditions()
self.$E.test.preConditions()
self.$F.test.preConditions()
self.$D.test.preConditions()
if !print("A") {
panic("pre/post condition failed")
}
return
}
} |
…into supun/generate-code-for-interfaces
Regarding the templating, that's a good idea! Agree that we can simplify these synthetic nodes generation through some templating mechanism 👌 |
This PR also includes the changes added in #3727, #3728, #3729, #3731. i.e: Builds on top of the foundation of those previous PRs.
Description
Generate code for interfaces separately, and avoids copying over the code for default functions and inherited function pre/post conditions.
Default functions
Instead, injects a delegator function, which would call into the interface's default method.
becomes:
Function conditions
A function condition that is defined in an interface method gets extracted out to a separate method. Then any implementation would call into this generated/synthetic function as part of their pre/post conditions.
e.g:
becomes
TODO:
master
branchFiles changed
in the Github PR explorer