Skip to content

Commit

Permalink
Fix desugaring inherited default functions that also have conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
SupunS committed Feb 5, 2025
1 parent e2a93e9 commit f4a14a8
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 16 deletions.
21 changes: 5 additions & 16 deletions bbq/compiler/desugar.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,11 +445,8 @@ func (d *Desugar) desugarPreConditions(
}
}

// If this is a method of a concrete-type, or an interface default method,
// then return with the updated statements, and continue desugaring the rest.

// TODO: Handle default functions with conditions

// If this is a method of a concrete-type then return with the updated statements,
// and continue desugaring the rest.
if d.canInlineConditions(funcBlock, conditions) {
return desugaredConditions
}
Expand Down Expand Up @@ -526,11 +523,8 @@ func (d *Desugar) desugarPostConditions(
}
}

// If this is a method of a concrete-type, or an interface default method,
// then return with the updated statements, and continue desugaring the rest.

// TODO: Handle default functions with conditions

// If this is a method of a concrete-type then return with the updated statements,
// and continue desugaring the rest.
if d.canInlineConditions(funcBlock, conditions) {
return desugaredConditions
}
Expand All @@ -550,16 +544,11 @@ func (d *Desugar) desugarPostConditions(
}

func (d *Desugar) canInlineConditions(funcBlock *ast.FunctionBlock, conditions *ast.Conditions) bool {

// Conditions can be inlined if one of the conditions are satisfied:
// - There are no conditions
// - This is a method of a concrete-type (i.e: enclosingInterfaceType is `nil`)
// - This method is an interface default method (i.e: funcBlock has statements)

return conditions == nil ||
d.enclosingInterfaceType == nil ||
funcBlock.HasStatements()

d.enclosingInterfaceType == nil
}

// Generates a separate function for the provided conditions.
Expand Down
105 changes: 105 additions & 0 deletions bbq/vm/test/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3539,4 +3539,109 @@ func TestDefaultFunctionsWithConditions(t *testing.T) {
}, logs,
)
})

t.Run("default and conditions in parent, more conditions in child", func(t *testing.T) {
t.Parallel()

storage := interpreter.NewInMemoryStorage(nil)

activation := sema.NewVariableActivation(sema.BaseValueActivation)
activation.DeclareValue(stdlib.PanicFunction)
activation.DeclareValue(stdlib.NewStandardLibraryStaticFunction(
"log",
sema.NewSimpleFunctionType(
sema.FunctionPurityView,
[]sema.Parameter{
{
Label: sema.ArgumentLabelNotRequired,
Identifier: "value",
TypeAnnotation: sema.AnyStructTypeAnnotation,
},
},
sema.VoidTypeAnnotation,
),
"",
nil,
))

var logs []string
vmConfig := &vm.Config{
Storage: storage,
AccountHandler: &testAccountHandler{},
NativeFunctionsProvider: func() map[string]vm.Value {
funcs := vm.NativeFunctions()
funcs[commons.LogFunctionName] = vm.NativeFunctionValue{
ParameterCount: len(stdlib.LogFunctionType.Parameters),
Function: func(config *vm.Config, typeArguments []interpreter.StaticType, arguments ...vm.Value) vm.Value {
logs = append(logs, arguments[0].String())
return vm.VoidValue{}
},
}

return funcs
},
}

_, err := compileAndInvokeWithOptions(t, `
struct interface Foo {
fun test(_ a: Int) {
pre {
printMessage("invoked Foo.test() pre-condition")
}
post {
printMessage("invoked Foo.test() post-condition")
}
printMessage("invoked Foo.test()")
}
}
struct interface Bar: Foo {
fun test(_ a: Int) {
pre {
printMessage("invoked Bar.test() pre-condition")
}
post {
printMessage("invoked Bar.test() post-condition")
}
}
}
struct Test: Bar {}
access(all) view fun printMessage(_ msg: String): Bool {
log(msg)
return true
}
fun main() {
Test().test(5)
}
`,
"main",
CompilerAndVMOptions{
VMConfig: vmConfig,
ParseAndCheckOptions: &ParseAndCheckOptions{
Config: &sema.Config{
LocationHandler: singleIdentifierLocationResolver(t),
BaseValueActivationHandler: func(location common.Location) *sema.VariableActivation {
return activation
},
},
},
},
)

require.NoError(t, err)
require.Equal(
t,
[]string{
"invoked Bar.test() pre-condition",
"invoked Foo.test() pre-condition",
"invoked Foo.test()",
"invoked Foo.test() post-condition",
"invoked Bar.test() post-condition",
}, logs,
)
})
}

0 comments on commit f4a14a8

Please sign in to comment.