Skip to content
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

Open
wants to merge 17 commits into
base: feature/compiler
Choose a base branch
from

Conversation

SupunS
Copy link
Member

@SupunS SupunS commented Jan 24, 2025

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.

struct interface IA {
    fun test(): Int {
        return 42
    }
}

struct Test: IA {}

becomes:

struct interface IA {
    fun test(): Int {
        return 42
    }
}

struct Test: IA {
    fun test(): Int {
        return parent.test()  // At codegen, this becomes a static function invocation
    }
}

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:

struct interface A {
    access(all) fun test(_ a: Int): Int {
        pre { a > 10: "a must be larger than 10" }
    }
}

struct interface B: A {
    access(all) fun test(_ a: Int): Int
}

struct C: B {
    fun test(_ a: Int): Int {
        return a + 3
    }
}

becomes

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 {}
}

struct C: B {
    fun test(_ a: Int): Int {
        parent.$A.test.preConditions(a)    // calls into the inherited pre-condition function (a static function invocation)
        return a + 3
    }
}

TODO:

  • Handle result variable inside conditions.
  • Handle situations where a default function with conditions gets overridden.

  • 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 requested a review from turbolent as a code owner January 24, 2025 00:59
@SupunS SupunS self-assigned this Jan 24, 2025
@SupunS SupunS force-pushed the supun/generate-code-for-interfaces branch from 269e3d6 to 2a13f25 Compare January 24, 2025 16:02
@SupunS SupunS changed the base branch from supun/improve-copied-code-compilation to feature/compiler January 24, 2025 16:09
@SupunS SupunS changed the title [Compiler+VM POC] Generate code for interfaces separately [Compiler+VM POC] Support inherited functions and conditions through delegation Jan 24, 2025
Copy link

github-actions bot commented Jan 24, 2025

Cadence Benchstat comparison

This branch with compared with the base branch onflow:feature/compiler commit 4703a34
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 force-pushed the supun/generate-code-for-interfaces branch from aed29ca to 5ded59f Compare January 24, 2025 16:29
@SupunS SupunS changed the title [Compiler+VM POC] Support inherited functions and conditions through delegation [Compiler] Support inherited functions and conditions through delegation Jan 24, 2025
@SupunS SupunS force-pushed the supun/generate-code-for-interfaces branch from 5c1c4c0 to 47355ef Compare January 24, 2025 19:55
@SupunS SupunS force-pushed the supun/generate-code-for-interfaces branch from 47355ef to b0570bb Compare January 24, 2025 19:58
@SupunS SupunS requested a review from jsproz January 24, 2025 20:46
@bluesign
Copy link
Contributor

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
    }
}

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