From 0cbf493840ca0addfe7e02e206c886ceb178251a Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Fri, 16 Jul 2021 19:56:21 +0200 Subject: [PATCH 01/43] Interface default implementation support --- runtime/interpreter/interpreter.go | 33 +++- runtime/sema/check_composite_declaration.go | 67 ++++++--- runtime/sema/check_interface_declaration.go | 59 +++----- runtime/sema/errors.go | 20 +++ runtime/sema/type.go | 3 +- runtime/tests/checker/interface_test.go | 141 ++++++++++++++++-- runtime/tests/interpreter/interpreter_test.go | 63 ++++++++ 7 files changed, 315 insertions(+), 71 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index e4d7f0dc84..d09e1fd1e2 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -206,6 +206,7 @@ type WrapperCode struct { InitializerFunctionWrapper FunctionWrapper DestructorFunctionWrapper FunctionWrapper FunctionWrappers map[string]FunctionWrapper + Functions map[string]FunctionValue } // TypeCodes is the value which stores the "prepared" / "callable" "code" @@ -1354,7 +1355,13 @@ func (interpreter *Interpreter) declareNonEnumCompositeValue( // the order does not matter. for name, functionWrapper := range code.FunctionWrappers { //nolint:maprangecheck + + if functions[name] == nil { + functions[name] = code.Functions[name] + } + functions[name] = functionWrapper(functions[name]) + } } @@ -1375,7 +1382,6 @@ func (interpreter *Interpreter) declareNonEnumCompositeValue( for i := len(typeRequirements) - 1; i >= 0; i-- { typeRequirement := typeRequirements[i] - wrapFunctions(interpreter.typeCodes.TypeRequirementCodes[typeRequirement.ID()]) } @@ -1668,6 +1674,29 @@ func (interpreter *Interpreter) compositeDestructorFunction( } } +func (interpreter *Interpreter) interfaceFunctions( + interfaceDeclaration *ast.InterfaceDeclaration, + lexicalScope *VariableActivation, +) map[string]FunctionValue { + + functions := map[string]FunctionValue{} + + for _, functionDeclaration := range interfaceDeclaration.Members.Functions() { + name := functionDeclaration.Identifier.Identifier + if functionDeclaration.FunctionBlock != nil { + if len(functionDeclaration.FunctionBlock.Block.Statements) > 0 { + functions[name] = + interpreter.compositeFunction( + functionDeclaration, + lexicalScope, + ) + } + } + } + + return functions +} + func (interpreter *Interpreter) compositeFunctions( compositeDeclaration *ast.CompositeDeclaration, lexicalScope *VariableActivation, @@ -2028,11 +2057,13 @@ func (interpreter *Interpreter) declareInterface( initializerFunctionWrapper := interpreter.initializerFunctionWrapper(declaration.Members, lexicalScope) destructorFunctionWrapper := interpreter.destructorFunctionWrapper(declaration.Members, lexicalScope) functionWrappers := interpreter.functionWrappers(declaration.Members, lexicalScope) + interfaceFunctions := interpreter.interfaceFunctions(declaration, lexicalScope) interpreter.typeCodes.InterfaceCodes[typeID] = WrapperCode{ InitializerFunctionWrapper: initializerFunctionWrapper, DestructorFunctionWrapper: destructorFunctionWrapper, FunctionWrappers: functionWrappers, + Functions: interfaceFunctions, } } diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index e5391b48af..09d7b26315 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -158,6 +158,8 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl checkMissingMembers := kind != ContainerKindInterface + overridden := map[string]bool{} + for i, interfaceType := range compositeType.ExplicitInterfaceConformances { interfaceNominalType := declaration.Conformances[i] @@ -170,6 +172,7 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl checkMissingMembers: checkMissingMembers, interfaceTypeIsTypeRequirement: false, }, + &overridden, ) } @@ -964,7 +967,9 @@ func (checker *Checker) checkCompositeConformance( interfaceType *InterfaceType, compositeKindMismatchIdentifier ast.Identifier, options compositeConformanceCheckOptions, + overridden *map[string]bool, ) { + var missingMembers []*Member var memberMismatches []MemberMismatch var missingNestedCompositeTypes []*CompositeType @@ -1017,16 +1022,31 @@ func (checker *Checker) checkCompositeConformance( if interfaceMember.Predeclared { return } - compositeMember, ok := compositeType.Members.Get(name) if !ok { if options.checkMissingMembers { - missingMembers = append(missingMembers, interfaceMember) + if interfaceMember.DeclarationKind != common.DeclarationKindFunction { + missingMembers = append(missingMembers, interfaceMember) + } else { + if !interfaceMember.HasImplementation { + missingMembers = append(missingMembers, interfaceMember) + } else { + if (*overridden)[name] { + checker.report( + &MultipleInterfaceDefaultImplementationsError{ + CompositeType: compositeType, + Member: interfaceMember, + }, + ) + } + (*overridden)[name] = true + } + + } } - return } - if !checker.memberSatisfied(compositeMember, interfaceMember) { + if compositeMember != nil && !checker.memberSatisfied(compositeMember, interfaceMember) { memberMismatches = append(memberMismatches, MemberMismatch{ CompositeMember: compositeMember, @@ -1034,6 +1054,7 @@ func (checker *Checker) checkCompositeConformance( }, ) } + }) // Determine missing nested composite type definitions @@ -1049,6 +1070,7 @@ func (checker *Checker) checkCompositeConformance( nestedCompositeType, ok := compositeType.nestedTypes.Get(name) if !ok { + missingNestedCompositeTypes = append(missingNestedCompositeTypes, requiredCompositeType) return } @@ -1075,13 +1097,13 @@ func (checker *Checker) checkCompositeConformance( }, ) } + } // TODO: return proper error func (checker *Checker) memberSatisfied(compositeMember, interfaceMember *Member) bool { // Check declaration kind - if compositeMember.DeclarationKind != interfaceMember.DeclarationKind { return false } @@ -1150,6 +1172,7 @@ func (checker *Checker) memberSatisfied(compositeMember, interfaceMember *Member return false } + } } @@ -1262,6 +1285,7 @@ func (checker *Checker) checkTypeRequirement( // like a top-level composite declaration to an interface type requiredInterfaceType := requiredCompositeType.InterfaceType() + overridden := map[string]bool{} checker.checkCompositeConformance( compositeDeclaration, @@ -1272,6 +1296,7 @@ func (checker *Checker) checkTypeRequirement( checkMissingMembers: true, interfaceTypeIsTypeRequirement: true, }, + &overridden, ) } @@ -1475,17 +1500,24 @@ func (checker *Checker) defaultMembersAndOrigins( ) } + hasImplementation := false + + if function.FunctionBlock != nil && len(function.FunctionBlock.Block.Statements) > 0 { + hasImplementation = true + } + members.Set( identifier, &Member{ - ContainerType: containerType, - Access: function.Access, - Identifier: function.Identifier, - DeclarationKind: declarationKind, - TypeAnnotation: fieldTypeAnnotation, - VariableKind: ast.VariableKindConstant, - ArgumentLabels: argumentLabels, - DocString: function.DocString, + ContainerType: containerType, + Access: function.Access, + Identifier: function.Identifier, + DeclarationKind: declarationKind, + TypeAnnotation: fieldTypeAnnotation, + VariableKind: ast.VariableKindConstant, + ArgumentLabels: argumentLabels, + DocString: function.DocString, + HasImplementation: hasImplementation, }) if checker.positionInfoEnabled && origins != nil { @@ -1730,15 +1762,6 @@ func (checker *Checker) checkSpecialFunction( ) switch containerKind { - case ContainerKindInterface: - if specialFunction.FunctionDeclaration.FunctionBlock != nil { - - checker.checkInterfaceSpecialFunctionBlock( - specialFunction.FunctionDeclaration.FunctionBlock, - containerDeclarationKind, - specialFunction.Kind, - ) - } case ContainerKindComposite: // Event declarations have an empty initializer as it is synthesized diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 6839380662..46fb31ca5e 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -177,22 +177,35 @@ func (checker *Checker) checkInterfaceFunctions( checker.declareSelfValue(selfType, selfDocString) + mustExit := false + checkResourceLoss := false + + if function.FunctionBlock != nil { + if len(function.FunctionBlock.Block.Statements) > 0 { + mustExit = true + checkResourceLoss = true + } else if (function.FunctionBlock.PreConditions == nil || len(*function.FunctionBlock.PreConditions) == 0) && + (function.FunctionBlock.PostConditions == nil || len(*function.FunctionBlock.PostConditions) == 0) { + + checker.report( + &InvalidImplementationError{ + Pos: function.FunctionBlock.StartPosition(), + ContainerKind: declarationKind, + ImplementedKind: common.DeclarationKindFunction, + }, + ) + } + } + checker.visitFunctionDeclaration( function, functionDeclarationOptions{ - mustExit: false, + mustExit: mustExit, declareFunction: false, - checkResourceLoss: false, + checkResourceLoss: checkResourceLoss, }, ) - if function.FunctionBlock != nil { - checker.checkInterfaceSpecialFunctionBlock( - function.FunctionBlock, - declarationKind, - common.DeclarationKindFunction, - ) - } }() } } @@ -338,31 +351,3 @@ func (checker *Checker) declareInterfaceMembers(declaration *ast.InterfaceDeclar checker.declareCompositeMembersAndValue(nestedCompositeDeclaration, ContainerKindInterface) } } - -func (checker *Checker) checkInterfaceSpecialFunctionBlock( - functionBlock *ast.FunctionBlock, - containerKind common.DeclarationKind, - implementedKind common.DeclarationKind, -) { - - statements := functionBlock.Block.Statements - if len(statements) > 0 { - checker.report( - &InvalidImplementationError{ - Pos: statements[0].StartPosition(), - ContainerKind: containerKind, - ImplementedKind: implementedKind, - }, - ) - } else if (functionBlock.PreConditions == nil || len(*functionBlock.PreConditions) == 0) && - (functionBlock.PostConditions == nil || len(*functionBlock.PostConditions) == 0) { - - checker.report( - &InvalidImplementationError{ - Pos: functionBlock.StartPosition(), - ContainerKind: containerKind, - ImplementedKind: implementedKind, - }, - ) - } -} diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 0ea5390359..d7aa526668 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1012,6 +1012,26 @@ func (e *DuplicateConformanceError) Error() string { func (*DuplicateConformanceError) isSemanticError() {} +// MultipleInterfaceDefaultImplementationsError + +// TODO: just make this a warning? + +type MultipleInterfaceDefaultImplementationsError struct { + CompositeType *CompositeType + Member *Member +} + +func (e *MultipleInterfaceDefaultImplementationsError) Error() string { + return fmt.Sprintf( + "%s `%s` has multiple interface default implementations for function `%s`", + e.CompositeType.Kind.Name(), + e.CompositeType.QualifiedString(), + e.Member.Identifier.Identifier, + ) +} + +func (*MultipleInterfaceDefaultImplementationsError) isSemanticError() {} + // MissingConformanceError type MissingConformanceError struct { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 8d1fd8a352..b449c41cab 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3553,7 +3553,8 @@ type Member struct { VariableKind ast.VariableKind ArgumentLabels []string // Predeclared fields can be considered initialized - Predeclared bool + Predeclared bool + HasImplementation bool // IgnoreInSerialization fields are ignored in serialization IgnoreInSerialization bool DocString string diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 10812e4d90..4c27fb91e0 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -133,7 +133,7 @@ func TestCheckInterfaceWithFunctionImplementationAndConditions(t *testing.T) { } } -func TestCheckInvalidInterfaceWithFunctionImplementation(t *testing.T) { +func TestCheckInterfaceWithFunctionImplementation(t *testing.T) { t.Parallel() @@ -145,17 +145,26 @@ func TestCheckInvalidInterfaceWithFunctionImplementation(t *testing.T) { ` %s interface Test { fun test(): Int { + pre{ + + } + post{ + + } return 1 } } + + %s TestUser: Test{ + + } `, - kind.Keyword(), + kind.Keyword(), kind.Keyword(), ), ) - errs := ExpectCheckerErrors(t, err, 1) + require.NoError(t, err) - assert.IsType(t, &sema.InvalidImplementationError{}, errs[0]) }) } } @@ -210,7 +219,7 @@ func TestCheckInterfaceWithInitializer(t *testing.T) { } } -func TestCheckInvalidInterfaceWithInitializerImplementation(t *testing.T) { +func TestCheckInterfaceWithInitializerImplementation(t *testing.T) { t.Parallel() @@ -230,9 +239,8 @@ func TestCheckInvalidInterfaceWithInitializerImplementation(t *testing.T) { ), ) - errs := ExpectCheckerErrors(t, err, 1) + require.NoError(t, err) - assert.IsType(t, &sema.InvalidImplementationError{}, errs[0]) }) } } @@ -1620,7 +1628,7 @@ func TestCheckContractInterfaceTypeRequirement(t *testing.T) { require.NoError(t, err) } -func TestCheckInvalidContractInterfaceTypeRequirementFunctionImplementation(t *testing.T) { +func TestCheckContractInterfaceTypeRequirementFunctionImplementation(t *testing.T) { t.Parallel() @@ -1636,9 +1644,8 @@ func TestCheckInvalidContractInterfaceTypeRequirementFunctionImplementation(t *t `, ) - errs := ExpectCheckerErrors(t, err, 1) + require.NoError(t, err) - assert.IsType(t, &sema.InvalidImplementationError{}, errs[0]) } func TestCheckInvalidContractInterfaceTypeRequirementMissingFunction(t *testing.T) { @@ -1991,3 +1998,117 @@ func TestCheckInvalidInterfaceUseAsTypeSuggestion(t *testing.T) { errs[0].(*sema.InvalidInterfaceTypeError).ExpectedType, ) } + +//MultipleInterfaceDefaultImplementation +func TestCheckInvalidMultipleInterfaceDefaultImplementation(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface InterfaceA { + fun test(): Int{ + return 41 + } + } + + struct interface InterfaceB { + fun test(): Int{ + return 41 + } + } + + struct Test: InterfaceA, InterfaceB { + + } + + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.MultipleInterfaceDefaultImplementationsError{}, errs[0]) + +} + +func TestCheckMultipleInterfaceDefaultImplementationWhenOverriden(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface InterfaceA { + fun test(): Int{ + return 41 + } + } + + struct interface InterfaceB { + fun test(): Int{ + return 41 + } + } + + struct Test: InterfaceA, InterfaceB { + fun test(): Int{ + return 42 + } + } + + + `) + + require.NoError(t, err) + +} + +func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface InterfaceA { + fun test(): Int{ + return 41 + } + } + + struct interface InterfaceB { + fun test(): Int + } + + struct Test: InterfaceA, InterfaceB { + + } + + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.ConformanceError{}, errs[0]) + +} + +func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridden(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface InterfaceA { + fun test(): Int{ + return 41 + } + } + + struct interface InterfaceB { + fun test(): Int + } + + struct Test: InterfaceA, InterfaceB { + fun test(): Int{ + return 42 + } + } + + + `) + require.NoError(t, err) + +} diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index e9efc44c35..c764963dbd 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -8294,3 +8294,66 @@ func BenchmarkNewInterpreter(b *testing.B) { } }) } + +//InterfaceDefaultImplementation +func TestInterpretInterfaceDefaultImplementation(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + + struct interface InterfaceA { + fun test(): Int{ + return 42 + } + } + + struct Test: InterfaceA { + + } + + fun main(): Int { + return Test().test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewIntValueFromInt64(42), + value, + ) +} + +func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + + struct interface InterfaceA { + fun test(): Int{ + return 41 + } + } + + struct Test: InterfaceA { + fun test(): Int{ + return 42 + } + } + + fun main(): Int { + return Test().test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewIntValueFromInt64(42), + value, + ) +} From a1ad1219fe435013892ce1014669eb11c329d495 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:16:55 +0100 Subject: [PATCH 02/43] Update runtime/tests/checker/interface_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/interface_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 4c27fb91e0..c7039403b2 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2092,7 +2092,7 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridde _, err := ParseAndCheck(t, ` struct interface InterfaceA { - fun test(): Int{ + fun test(): Int { return 41 } } @@ -2101,8 +2101,8 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridde fun test(): Int } - struct Test: InterfaceA, InterfaceB { - fun test(): Int{ + struct Test: InterfaceA, InterfaceB { + fun test(): Int { return 42 } } From 366448bd0657d2467b5a1a80887520b835d4a1eb Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:17:03 +0100 Subject: [PATCH 03/43] Update runtime/tests/interpreter/interpreter_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/interpreter/interpreter_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index c764963dbd..d8b291271e 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -8333,13 +8333,13 @@ func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { inter := parseCheckAndInterpret(t, ` struct interface InterfaceA { - fun test(): Int{ + fun test(): Int { return 41 } } - struct Test: InterfaceA { - fun test(): Int{ + struct Test: InterfaceA { + fun test(): Int { return 42 } } From fb45f5212154ee2794c899dd87be054adb62bd33 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:17:11 +0100 Subject: [PATCH 04/43] Update runtime/tests/interpreter/interpreter_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/interpreter/interpreter_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index d8b291271e..692d52fbf5 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -8303,12 +8303,12 @@ func TestInterpretInterfaceDefaultImplementation(t *testing.T) { inter := parseCheckAndInterpret(t, ` struct interface InterfaceA { - fun test(): Int{ + fun test(): Int { return 42 } } - struct Test: InterfaceA { + struct Test: InterfaceA { } From ffb612cc5933224b67b9873d702e0bac9ac36c19 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:17:18 +0100 Subject: [PATCH 05/43] Update runtime/tests/checker/interface_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/interface_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index c7039403b2..d76f1dff48 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2065,7 +2065,7 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T _, err := ParseAndCheck(t, ` struct interface InterfaceA { - fun test(): Int{ + fun test(): Int { return 41 } } @@ -2074,7 +2074,7 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T fun test(): Int } - struct Test: InterfaceA, InterfaceB { + struct Test: InterfaceA, InterfaceB { } From 2edcd7fdbea19bdd12b946b80ad24408bc056865 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:21:28 +0100 Subject: [PATCH 06/43] tabs to spaces in code literals --- runtime/tests/interpreter/interpreter_test.go | 142 +++++++++--------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 692d52fbf5..86c3acbe96 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -4368,8 +4368,8 @@ func TestInterpretArrayAppendAll(t *testing.T) { inter := parseCheckAndInterpret(t, ` fun test(): [Int] { let a = [1, 2] - a.appendAll([3, 4]) - return a + a.appendAll([3, 4]) + return a } `) @@ -4395,8 +4395,8 @@ func TestInterpretArrayAppendAllBound(t *testing.T) { fun test(): [Int] { let a = [1, 2] let b = a.appendAll - b([3, 4]) - return a + b([3, 4]) + return a } `) @@ -4472,8 +4472,8 @@ func TestInterpretArrayConcatDoesNotModifyOriginalArray(t *testing.T) { inter := parseCheckAndInterpret(t, ` fun test(): [Int] { let a = [1, 2] - a.concat([3, 4]) - return a + a.concat([3, 4]) + return a } `) @@ -4867,18 +4867,18 @@ func TestInterpretDictionaryContainsKey(t *testing.T) { inter := parseCheckAndInterpret(t, ` fun doesContainKey(): Bool { - let x = { - 1: "one", - 2: "two" - } + let x = { + 1: "one", + 2: "two" + } return x.containsKey(1) } fun doesNotContainKey(): Bool { - let x = { - 1: "one", - 2: "two" - } + let x = { + 1: "one", + 2: "two" + } return x.containsKey(3) } `) @@ -6851,7 +6851,7 @@ func TestInterpretConformToImportedInterface(t *testing.T) { } } } - `, + `, checker.ParseAndCheckOptions{ Location: ImportedLocation, }, @@ -7420,14 +7420,14 @@ func TestInterpretResourceAssignmentForceTransfer(t *testing.T) { t.Run("new to non-nil", func(t *testing.T) { inter := parseCheckAndInterpret(t, ` - resource X {} + resource X {} - fun test() { - var x: @X? <- create X() - x <-! create X() - destroy x - } - `) + fun test() { + var x: @X? <- create X() + x <-! create X() + destroy x + } + `) _, err := inter.Invoke("test") require.Error(t, err) @@ -7438,15 +7438,15 @@ func TestInterpretResourceAssignmentForceTransfer(t *testing.T) { t.Run("existing to nil", func(t *testing.T) { inter := parseCheckAndInterpret(t, ` - resource X {} + resource X {} - fun test() { - let x <- create X() - var x2: @X? <- nil - x2 <-! x - destroy x2 - } - `) + fun test() { + let x <- create X() + var x2: @X? <- nil + x2 <-! x + destroy x2 + } + `) _, err := inter.Invoke("test") require.NoError(t, err) @@ -7455,15 +7455,15 @@ func TestInterpretResourceAssignmentForceTransfer(t *testing.T) { t.Run("existing to non-nil", func(t *testing.T) { inter := parseCheckAndInterpret(t, ` - resource X {} + resource X {} - fun test() { - let x <- create X() - var x2: @X? <- create X() - x2 <-! x - destroy x2 - } - `) + fun test() { + let x <- create X() + var x2: @X? <- create X() + x2 <-! x + destroy x2 + } + `) _, err := inter.Invoke("test") require.Error(t, err) @@ -7474,9 +7474,9 @@ func TestInterpretResourceAssignmentForceTransfer(t *testing.T) { t.Run("force-assignment initialization", func(t *testing.T) { inter := parseCheckAndInterpret(t, ` - resource X {} + resource X {} - resource Y { + resource Y { var x: @X? @@ -7489,11 +7489,11 @@ func TestInterpretResourceAssignmentForceTransfer(t *testing.T) { } } - fun test() { - let y <- create Y() - destroy y - } - `) + fun test() { + let y <- create Y() + destroy y + } + `) _, err := inter.Invoke("test") require.NoError(t, err) @@ -8302,19 +8302,19 @@ func TestInterpretInterfaceDefaultImplementation(t *testing.T) { inter := parseCheckAndInterpret(t, ` - struct interface InterfaceA { - fun test(): Int { - return 42 - } - } - - struct Test: InterfaceA { - - } + struct interface InterfaceA { + fun test(): Int { + return 42 + } + } + + struct Test: InterfaceA { + + } - fun main(): Int { - return Test().test() - } + fun main(): Int { + return Test().test() + } `) value, err := inter.Invoke("main") @@ -8332,21 +8332,21 @@ func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { inter := parseCheckAndInterpret(t, ` - struct interface InterfaceA { - fun test(): Int { - return 41 - } - } - - struct Test: InterfaceA { - fun test(): Int { - return 42 - } - } + struct interface InterfaceA { + fun test(): Int { + return 41 + } + } + + struct Test: InterfaceA { + fun test(): Int { + return 42 + } + } - fun main(): Int { - return Test().test() - } + fun main(): Int { + return Test().test() + } `) value, err := inter.Invoke("main") From df52dd23f71429442f0ab4d20a96743acb19d200 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:21:51 +0100 Subject: [PATCH 07/43] Update runtime/tests/checker/interface_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/interface_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index d76f1dff48..fc527b8e60 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2041,13 +2041,13 @@ func TestCheckMultipleInterfaceDefaultImplementationWhenOverriden(t *testing.T) } struct interface InterfaceB { - fun test(): Int{ + fun test(): Int { return 41 } } - struct Test: InterfaceA, InterfaceB { - fun test(): Int{ + struct Test: InterfaceA, InterfaceB { + fun test(): Int { return 42 } } From 195387f6625a4bce58526291349add80103cdce4 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:22:10 +0100 Subject: [PATCH 08/43] Update runtime/tests/checker/interface_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/interface_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index fc527b8e60..0009549b5c 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2006,13 +2006,13 @@ func TestCheckInvalidMultipleInterfaceDefaultImplementation(t *testing.T) { _, err := ParseAndCheck(t, ` struct interface InterfaceA { - fun test(): Int{ + fun test(): Int { return 41 } } struct interface InterfaceB { - fun test(): Int{ + fun test(): Int { return 41 } } From a1c80868858df23c38c15ed702f191e56c2a15ff Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:22:24 +0100 Subject: [PATCH 09/43] Update runtime/tests/checker/interface_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/interface_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 0009549b5c..71815acbd7 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -155,7 +155,7 @@ func TestCheckInterfaceWithFunctionImplementation(t *testing.T) { } } - %s TestUser: Test{ + %[1]s TestUser: Test{ } `, From 6cc3263e1533e05b50e6c666bfd3a30f5ab34e3a Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:22:40 +0100 Subject: [PATCH 10/43] Update runtime/tests/checker/interface_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/interface_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 71815acbd7..7d8f040c5c 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -143,7 +143,7 @@ func TestCheckInterfaceWithFunctionImplementation(t *testing.T) { _, err := ParseAndCheck(t, fmt.Sprintf( ` - %s interface Test { + %[1]s interface Test { fun test(): Int { pre{ From b75c3c52779e687f244aa4d759f6cb124b677d08 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:23:51 +0100 Subject: [PATCH 11/43] Update runtime/tests/checker/interface_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/interface_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 7d8f040c5c..7ea5966bb4 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -159,7 +159,7 @@ func TestCheckInterfaceWithFunctionImplementation(t *testing.T) { } `, - kind.Keyword(), kind.Keyword(), + kind.Keyword(), ), ) From 772916a5fc3462f8a6cb907ea987a9ec683d5c4d Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:25:33 +0100 Subject: [PATCH 12/43] tabs to spaces in code literals --- runtime/tests/checker/interface_test.go | 128 +++++++++++------------- 1 file changed, 61 insertions(+), 67 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 7ea5966bb4..20573c6bf3 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -145,19 +145,13 @@ func TestCheckInterfaceWithFunctionImplementation(t *testing.T) { ` %[1]s interface Test { fun test(): Int { - pre{ - - } - post{ - - } return 1 } } - - %[1]s TestUser: Test{ + + %[1]s TestUser: Test{ - } + } `, kind.Keyword(), ), @@ -2005,21 +1999,21 @@ func TestCheckInvalidMultipleInterfaceDefaultImplementation(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` - struct interface InterfaceA { - fun test(): Int { - return 41 - } - } - - struct interface InterfaceB { - fun test(): Int { - return 41 - } - } - - struct Test: InterfaceA, InterfaceB { - - } + struct interface InterfaceA { + fun test(): Int { + return 41 + } + } + + struct interface InterfaceB { + fun test(): Int { + return 41 + } + } + + struct Test: InterfaceA, InterfaceB { + + } `) @@ -2034,23 +2028,23 @@ func TestCheckMultipleInterfaceDefaultImplementationWhenOverriden(t *testing.T) t.Parallel() _, err := ParseAndCheck(t, ` - struct interface InterfaceA { - fun test(): Int{ - return 41 - } - } - - struct interface InterfaceB { - fun test(): Int { - return 41 - } - } - - struct Test: InterfaceA, InterfaceB { - fun test(): Int { - return 42 - } - } + struct interface InterfaceA { + fun test(): Int{ + return 41 + } + } + + struct interface InterfaceB { + fun test(): Int { + return 41 + } + } + + struct Test: InterfaceA, InterfaceB { + fun test(): Int { + return 42 + } + } `) @@ -2064,19 +2058,19 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T t.Parallel() _, err := ParseAndCheck(t, ` - struct interface InterfaceA { - fun test(): Int { - return 41 - } - } + struct interface InterfaceA { + fun test(): Int { + return 41 + } + } - struct interface InterfaceB { - fun test(): Int - } - - struct Test: InterfaceA, InterfaceB { + struct interface InterfaceB { + fun test(): Int + } + + struct Test: InterfaceA, InterfaceB { - } + } `) @@ -2091,21 +2085,21 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridde t.Parallel() _, err := ParseAndCheck(t, ` - struct interface InterfaceA { - fun test(): Int { - return 41 - } - } - - struct interface InterfaceB { - fun test(): Int - } - - struct Test: InterfaceA, InterfaceB { - fun test(): Int { - return 42 - } - } + struct interface InterfaceA { + fun test(): Int { + return 41 + } + } + + struct interface InterfaceB { + fun test(): Int + } + + struct Test: InterfaceA, InterfaceB { + fun test(): Int { + return 42 + } + } `) From 7504b0ccf617970b4d85629d604d8480b283d5e7 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:45:36 +0100 Subject: [PATCH 13/43] Update runtime/sema/check_composite_declaration.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/sema/check_composite_declaration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 09d7b26315..341731a74b 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1031,7 +1031,7 @@ func (checker *Checker) checkCompositeConformance( if !interfaceMember.HasImplementation { missingMembers = append(missingMembers, interfaceMember) } else { - if (*overridden)[name] { + if _, isOverridden := overridden[name]; isOverridden { checker.report( &MultipleInterfaceDefaultImplementationsError{ CompositeType: compositeType, From 636f5a9526b636f96545d3daf2cec867facdec22 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 20:45:50 +0100 Subject: [PATCH 14/43] Update runtime/sema/check_composite_declaration.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/sema/check_composite_declaration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 341731a74b..831e1b6e63 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1039,7 +1039,7 @@ func (checker *Checker) checkCompositeConformance( }, ) } - (*overridden)[name] = true + overridden[name] = struct{} } } From 2151f1c3ec1cd06b637ae058bff6278f14121ff9 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Thu, 25 Nov 2021 21:34:35 +0100 Subject: [PATCH 15/43] changes to comments --- runtime/ast/block.go | 8 ++++++++ runtime/interpreter/interpreter.go | 10 ++++++++-- runtime/sema/check_composite_declaration.go | 22 +++++++++++---------- runtime/sema/check_interface_declaration.go | 4 ++-- runtime/sema/errors.go | 8 ++++++++ 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/runtime/ast/block.go b/runtime/ast/block.go index 62f51b9976..df3df96143 100644 --- a/runtime/ast/block.go +++ b/runtime/ast/block.go @@ -85,6 +85,10 @@ func (b *FunctionBlock) EndPosition() Position { return b.Block.EndPos } +func (b *FunctionBlock) HasStatements() bool { + return b != nil && len(b.Block.Statements) > 0 +} + // Condition type Condition struct { @@ -96,3 +100,7 @@ type Condition struct { // Conditions type Conditions []*Condition + +func (c *Conditions) IsEmpty() bool { + return c == nil || len(*c) == 0 +} diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index d09e1fd1e2..b93673821d 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -1679,9 +1679,15 @@ func (interpreter *Interpreter) interfaceFunctions( lexicalScope *VariableActivation, ) map[string]FunctionValue { - functions := map[string]FunctionValue{} + functionDeclarations := interfaceDeclaration.Members.Functions() + functionCount := len(functionDeclarations) + + var functions map[string]FunctionValue + if functionCount > 0 { + functions = make(map[string]FunctionValue, functionCount) + } - for _, functionDeclaration := range interfaceDeclaration.Members.Functions() { + for _, functionDeclaration := range functionDeclarations { name := functionDeclaration.Identifier.Identifier if functionDeclaration.FunctionBlock != nil { if len(functionDeclaration.FunctionBlock.Block.Statements) > 0 { diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 831e1b6e63..f1af5b8477 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -158,7 +158,7 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl checkMissingMembers := kind != ContainerKindInterface - overridden := map[string]bool{} + overridden := map[string]struct{}{} for i, interfaceType := range compositeType.ExplicitInterfaceConformances { interfaceNominalType := declaration.Conformances[i] @@ -172,7 +172,7 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl checkMissingMembers: checkMissingMembers, interfaceTypeIsTypeRequirement: false, }, - &overridden, + overridden, ) } @@ -967,7 +967,7 @@ func (checker *Checker) checkCompositeConformance( interfaceType *InterfaceType, compositeKindMismatchIdentifier ast.Identifier, options compositeConformanceCheckOptions, - overridden *map[string]bool, + overridden map[string]struct{}, ) { var missingMembers []*Member @@ -1025,9 +1025,9 @@ func (checker *Checker) checkCompositeConformance( compositeMember, ok := compositeType.Members.Get(name) if !ok { if options.checkMissingMembers { - if interfaceMember.DeclarationKind != common.DeclarationKindFunction { - missingMembers = append(missingMembers, interfaceMember) - } else { + + if interfaceMember.DeclarationKind == common.DeclarationKindFunction { + if !interfaceMember.HasImplementation { missingMembers = append(missingMembers, interfaceMember) } else { @@ -1039,9 +1039,11 @@ func (checker *Checker) checkCompositeConformance( }, ) } - overridden[name] = struct{} + overridden[name] = struct{}{} } + } else { + missingMembers = append(missingMembers, interfaceMember) } } } @@ -1285,7 +1287,7 @@ func (checker *Checker) checkTypeRequirement( // like a top-level composite declaration to an interface type requiredInterfaceType := requiredCompositeType.InterfaceType() - overridden := map[string]bool{} + overridden := map[string]struct{}{} checker.checkCompositeConformance( compositeDeclaration, @@ -1296,7 +1298,7 @@ func (checker *Checker) checkTypeRequirement( checkMissingMembers: true, interfaceTypeIsTypeRequirement: true, }, - &overridden, + overridden, ) } @@ -1502,7 +1504,7 @@ func (checker *Checker) defaultMembersAndOrigins( hasImplementation := false - if function.FunctionBlock != nil && len(function.FunctionBlock.Block.Statements) > 0 { + if function.FunctionBlock.HasStatements() { hasImplementation = true } diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 46fb31ca5e..81280121ed 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -184,8 +184,8 @@ func (checker *Checker) checkInterfaceFunctions( if len(function.FunctionBlock.Block.Statements) > 0 { mustExit = true checkResourceLoss = true - } else if (function.FunctionBlock.PreConditions == nil || len(*function.FunctionBlock.PreConditions) == 0) && - (function.FunctionBlock.PostConditions == nil || len(*function.FunctionBlock.PostConditions) == 0) { + } else if function.FunctionBlock.PreConditions.IsEmpty() && + function.FunctionBlock.PostConditions.IsEmpty() { checker.report( &InvalidImplementationError{ diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index d7aa526668..ca9eece1aa 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1030,6 +1030,14 @@ func (e *MultipleInterfaceDefaultImplementationsError) Error() string { ) } +func (e *MultipleInterfaceDefaultImplementationsError) StartPosition() ast.Position { + return e.Member.Identifier.StartPosition() +} + +func (e *MultipleInterfaceDefaultImplementationsError) EndPosition() ast.Position { + return e.Member.Identifier.EndPosition() +} + func (*MultipleInterfaceDefaultImplementationsError) isSemanticError() {} // MissingConformanceError From 3099f378a1fdcfaadd1b06a8c8bed9b163f8d479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 08:43:30 -0800 Subject: [PATCH 16/43] apply default functions in separate loop, ranging over interface functions --- runtime/interpreter/interpreter.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index b93673821d..ecfddb7242 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -1348,20 +1348,30 @@ func (interpreter *Interpreter) declareNonEnumCompositeValue( destructorFunction = destructorFunctionWrapper(destructorFunction) } - // Wrap functions + // Apply default functions, if conforming type does not provide the function // Iterating over the map in a non-deterministic way is OK, // we only apply the function wrapper to each function, // the order does not matter. - for name, functionWrapper := range code.FunctionWrappers { //nolint:maprangecheck - - if functions[name] == nil { - functions[name] = code.Functions[name] + for name, function := range code.Functions { //nolint:maprangecheck + if functions[name] != nil { + continue } + if functions == nil { + functions = map[string]FunctionValue{} + } + functions[name] = function + } - functions[name] = functionWrapper(functions[name]) + // Wrap functions + // Iterating over the map in a non-deterministic way is OK, + // we only apply the function wrapper to each function, + // the order does not matter. + + for name, functionWrapper := range code.FunctionWrappers { //nolint:maprangecheck + functions[name] = functionWrapper(functions[name]) } } From b189f12ee3a2e26cbb7f65675add42271776aad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 08:43:40 -0800 Subject: [PATCH 17/43] simplify --- runtime/interpreter/interpreter.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index ecfddb7242..b9d20fe3a7 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -1699,14 +1699,12 @@ func (interpreter *Interpreter) interfaceFunctions( for _, functionDeclaration := range functionDeclarations { name := functionDeclaration.Identifier.Identifier - if functionDeclaration.FunctionBlock != nil { - if len(functionDeclaration.FunctionBlock.Block.Statements) > 0 { - functions[name] = - interpreter.compositeFunction( - functionDeclaration, - lexicalScope, - ) - } + if functionDeclaration.FunctionBlock.HasStatements() { + functions[name] = + interpreter.compositeFunction( + functionDeclaration, + lexicalScope, + ) } } From 0b227fa664239f6b7350603484735bffefe934f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 08:46:07 -0800 Subject: [PATCH 18/43] simplify and document function inheriting from interface --- runtime/sema/check_composite_declaration.go | 80 ++++++++++++--------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index f1af5b8477..7e939f1d7a 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -158,7 +158,7 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl checkMissingMembers := kind != ContainerKindInterface - overridden := map[string]struct{}{} + inherited := map[string]struct{}{} for i, interfaceType := range compositeType.ExplicitInterfaceConformances { interfaceNominalType := declaration.Conformances[i] @@ -172,7 +172,7 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl checkMissingMembers: checkMissingMembers, interfaceTypeIsTypeRequirement: false, }, - overridden, + inherited, ) } @@ -961,13 +961,19 @@ type compositeConformanceCheckOptions struct { interfaceTypeIsTypeRequirement bool } +// checkCompositeConformance checks if the given composite declaration with the given composite type +// conforms to the specified interface type. +// +// inherited is an "input/output parameter": It tracks which member were inherited from the interface. +// It allows tracking this across conformance checks of multiple interfaces. +// func (checker *Checker) checkCompositeConformance( compositeDeclaration *ast.CompositeDeclaration, compositeType *CompositeType, interfaceType *InterfaceType, compositeKindMismatchIdentifier ast.Identifier, options compositeConformanceCheckOptions, - overridden map[string]struct{}, + inherited map[string]struct{}, ) { var missingMembers []*Member @@ -1022,39 +1028,43 @@ func (checker *Checker) checkCompositeConformance( if interfaceMember.Predeclared { return } + compositeMember, ok := compositeType.Members.Get(name) - if !ok { - if options.checkMissingMembers { - - if interfaceMember.DeclarationKind == common.DeclarationKindFunction { - - if !interfaceMember.HasImplementation { - missingMembers = append(missingMembers, interfaceMember) - } else { - if _, isOverridden := overridden[name]; isOverridden { - checker.report( - &MultipleInterfaceDefaultImplementationsError{ - CompositeType: compositeType, - Member: interfaceMember, - }, - ) - } - overridden[name] = struct{}{} - } - - } else { - missingMembers = append(missingMembers, interfaceMember) - } + if ok { + + // If the composite member exists, check if it satisfies the mem + + if compositeMember != nil && !checker.memberSatisfied(compositeMember, interfaceMember) { + memberMismatches = append(memberMismatches, + MemberMismatch{ + CompositeMember: compositeMember, + InterfaceMember: interfaceMember, + }, + ) } - } - if compositeMember != nil && !checker.memberSatisfied(compositeMember, interfaceMember) { - memberMismatches = append(memberMismatches, - MemberMismatch{ - CompositeMember: compositeMember, - InterfaceMember: interfaceMember, - }, - ) + } else if options.checkMissingMembers { + + // If the composite member does not exist, the interface may provide a default function. + // However, only one of the composite's conformances (interfaces) + // may provide a default function. + + if interfaceMember.DeclarationKind == common.DeclarationKindFunction && + interfaceMember.HasImplementation { + + if _, ok := inherited[name]; ok { + checker.report( + &MultipleInterfaceDefaultImplementationsError{ + CompositeType: compositeType, + Member: interfaceMember, + }, + ) + } + inherited[name] = struct{}{} + + } else { + missingMembers = append(missingMembers, interfaceMember) + } } }) @@ -1287,7 +1297,7 @@ func (checker *Checker) checkTypeRequirement( // like a top-level composite declaration to an interface type requiredInterfaceType := requiredCompositeType.InterfaceType() - overridden := map[string]struct{}{} + inherited := map[string]struct{}{} checker.checkCompositeConformance( compositeDeclaration, @@ -1298,7 +1308,7 @@ func (checker *Checker) checkTypeRequirement( checkMissingMembers: true, interfaceTypeIsTypeRequirement: true, }, - overridden, + inherited, ) } From fa720f6729f7f874143768ac201336343f63432d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 08:46:20 -0800 Subject: [PATCH 19/43] simplify --- runtime/sema/check_composite_declaration.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 7e939f1d7a..50d044616e 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1512,11 +1512,7 @@ func (checker *Checker) defaultMembersAndOrigins( ) } - hasImplementation := false - - if function.FunctionBlock.HasStatements() { - hasImplementation = true - } + hasImplementation := function.FunctionBlock.HasStatements() members.Set( identifier, From da610f955ce61ec21116d72fb8774a94c8b3e02b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 08:46:25 -0800 Subject: [PATCH 20/43] simplify --- runtime/sema/check_interface_declaration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 81280121ed..b7e8a39f8f 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -181,7 +181,7 @@ func (checker *Checker) checkInterfaceFunctions( checkResourceLoss := false if function.FunctionBlock != nil { - if len(function.FunctionBlock.Block.Statements) > 0 { + if function.FunctionBlock.HasStatements() { mustExit = true checkResourceLoss = true } else if function.FunctionBlock.PreConditions.IsEmpty() && From 82597c556d488125b733623a61161c3a68dda4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 12:44:36 -0800 Subject: [PATCH 21/43] Apply suggestions from code review --- runtime/sema/errors.go | 4 +--- runtime/tests/checker/interface_test.go | 1 - runtime/tests/interpreter/interpreter_test.go | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index ca9eece1aa..3445cd638a 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1013,9 +1013,7 @@ func (e *DuplicateConformanceError) Error() string { func (*DuplicateConformanceError) isSemanticError() {} // MultipleInterfaceDefaultImplementationsError - -// TODO: just make this a warning? - +// type MultipleInterfaceDefaultImplementationsError struct { CompositeType *CompositeType Member *Member diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 20573c6bf3..8a097b1cfe 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -1993,7 +1993,6 @@ func TestCheckInvalidInterfaceUseAsTypeSuggestion(t *testing.T) { ) } -//MultipleInterfaceDefaultImplementation func TestCheckInvalidMultipleInterfaceDefaultImplementation(t *testing.T) { t.Parallel() diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 86c3acbe96..f096686414 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -8295,7 +8295,6 @@ func BenchmarkNewInterpreter(b *testing.B) { }) } -//InterfaceDefaultImplementation func TestInterpretInterfaceDefaultImplementation(t *testing.T) { t.Parallel() From 01af2b344e5b5175b468d952b1c44a8ea1a91f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 12:54:38 -0800 Subject: [PATCH 22/43] move tests to separate file --- runtime/tests/interpreter/interface_test.go | 90 +++++++++++++++++++ runtime/tests/interpreter/interpreter_test.go | 62 ------------- 2 files changed, 90 insertions(+), 62 deletions(-) create mode 100644 runtime/tests/interpreter/interface_test.go diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go new file mode 100644 index 0000000000..9d98339966 --- /dev/null +++ b/runtime/tests/interpreter/interface_test.go @@ -0,0 +1,90 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright 2019-2020 Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence/runtime/interpreter" +) + +func TestInterpretInterfaceDefaultImplementation(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + + struct interface InterfaceA { + fun test(): Int { + return 42 + } + } + + struct Test: InterfaceA { + + } + + fun main(): Int { + return Test().test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewIntValueFromInt64(42), + value, + ) +} + +func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + + struct interface InterfaceA { + fun test(): Int { + return 41 + } + } + + struct Test: InterfaceA { + fun test(): Int { + return 42 + } + } + + fun main(): Int { + return Test().test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewIntValueFromInt64(42), + value, + ) +} diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 0c460f3dba..8f5f5a0f3e 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -9615,65 +9615,3 @@ func TestInterpretArrayTypeInference(t *testing.T) { ) }) } - -func TestInterpretInterfaceDefaultImplementation(t *testing.T) { - - t.Parallel() - - inter := parseCheckAndInterpret(t, ` - - struct interface InterfaceA { - fun test(): Int { - return 42 - } - } - - struct Test: InterfaceA { - - } - - fun main(): Int { - return Test().test() - } - `) - - value, err := inter.Invoke("main") - require.NoError(t, err) - - assert.Equal(t, - interpreter.NewIntValueFromInt64(42), - value, - ) -} - -func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { - - t.Parallel() - - inter := parseCheckAndInterpret(t, ` - - struct interface InterfaceA { - fun test(): Int { - return 41 - } - } - - struct Test: InterfaceA { - fun test(): Int { - return 42 - } - } - - fun main(): Int { - return Test().test() - } - `) - - value, err := inter.Invoke("main") - require.NoError(t, err) - - assert.Equal(t, - interpreter.NewIntValueFromInt64(42), - value, - ) -} From a79f3085aa94881426e3187ed7b5b9ed24aa92ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 13:02:35 -0800 Subject: [PATCH 23/43] clean up tests --- runtime/tests/checker/interface_test.go | 115 +++++++++++------------- 1 file changed, 53 insertions(+), 62 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 8a097b1cfe..8e8f33f97b 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -148,7 +148,7 @@ func TestCheckInterfaceWithFunctionImplementation(t *testing.T) { return 1 } } - + %[1]s TestUser: Test{ } @@ -1998,28 +1998,26 @@ func TestCheckInvalidMultipleInterfaceDefaultImplementation(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` - struct interface InterfaceA { - fun test(): Int { - return 41 - } - } - - struct interface InterfaceB { - fun test(): Int { - return 41 - } - } - - struct Test: InterfaceA, InterfaceB { - - } + struct interface IA { + fun test(): Int { + return 41 + } + } + + struct interface IB { + fun test(): Int { + return 41 + } + } + + struct Test: IA, IB { + } `) errs := ExpectCheckerErrors(t, err, 1) require.IsType(t, &sema.MultipleInterfaceDefaultImplementationsError{}, errs[0]) - } func TestCheckMultipleInterfaceDefaultImplementationWhenOverriden(t *testing.T) { @@ -2027,29 +2025,26 @@ func TestCheckMultipleInterfaceDefaultImplementationWhenOverriden(t *testing.T) t.Parallel() _, err := ParseAndCheck(t, ` - struct interface InterfaceA { - fun test(): Int{ - return 41 - } - } - - struct interface InterfaceB { - fun test(): Int { - return 41 - } - } - - struct Test: InterfaceA, InterfaceB { - fun test(): Int { - return 42 - } - } + struct interface IA { + fun test(): Int{ + return 41 + } + } + struct interface IB { + fun test(): Int { + return 41 + } + } + struct Test: IA, IB { + fun test(): Int { + return 42 + } + } `) require.NoError(t, err) - } func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T) { @@ -2057,26 +2052,24 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T t.Parallel() _, err := ParseAndCheck(t, ` - struct interface InterfaceA { - fun test(): Int { - return 41 - } - } + struct interface IA { + fun test(): Int { + return 41 + } + } - struct interface InterfaceB { - fun test(): Int - } - - struct Test: InterfaceA, InterfaceB { + struct interface IB { + fun test(): Int + } - } + struct Test: IA, IB { + } `) errs := ExpectCheckerErrors(t, err, 1) require.IsType(t, &sema.ConformanceError{}, errs[0]) - } func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridden(t *testing.T) { @@ -2084,23 +2077,21 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridde t.Parallel() _, err := ParseAndCheck(t, ` - struct interface InterfaceA { - fun test(): Int { - return 41 - } - } - - struct interface InterfaceB { - fun test(): Int - } - - struct Test: InterfaceA, InterfaceB { - fun test(): Int { - return 42 - } - } + struct interface IA { + fun test(): Int { + return 41 + } + } + struct interface IB { + fun test(): Int + } + struct Test: IA, IB { + fun test(): Int { + return 42 + } + } `) require.NoError(t, err) From 22aab34810c6701016dc4b3308aa7cbcba577d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 13:02:43 -0800 Subject: [PATCH 24/43] add more tests --- runtime/tests/checker/interface_test.go | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 8e8f33f97b..96779bb70e 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2094,5 +2094,53 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridde } `) require.NoError(t, err) +} + +func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + fun test(): Int { + return self.x + } + } + + struct Test: IA { + let x: Int + + init(x: Int) { + self.x = x + } + } + `) + + errs := ExpectCheckerErrors(t, err, 1) + require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0]) +} + +func TestCheckInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + let x: Int + + fun test(): Int { + return self.x + } + } + + struct Test: IA { + let x: Int + + init(x: Int) { + self.x = x + } + } + `) + require.NoError(t, err) } From ad4f649837607d82608afc0883ce3c27483399e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 16:15:55 -0800 Subject: [PATCH 25/43] add tests for type requirements --- runtime/tests/checker/conformance_test.go | 37 ++ runtime/tests/checker/interface_test.go | 531 +++++++++++++++++--- runtime/tests/interpreter/interface_test.go | 165 ++++-- 3 files changed, 622 insertions(+), 111 deletions(-) diff --git a/runtime/tests/checker/conformance_test.go b/runtime/tests/checker/conformance_test.go index 4ac4d11897..44785055d2 100644 --- a/runtime/tests/checker/conformance_test.go +++ b/runtime/tests/checker/conformance_test.go @@ -460,3 +460,40 @@ func TestCheckTypeRequirementDuplicateDeclaration(t *testing.T) { require.IsType(t, &sema.ConformanceError{}, errs[11]) require.IsType(t, &sema.ConformanceError{}, errs[12]) } + +func TestCheckMultipleTypeRequirements(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + let a: Int + } + } + + contract interface IB { + + struct X { + let b: Int + } + } + + contract Test: IA, IB { + + struct X { + let a: Int + // missing b + + init() { + self.a = 0 + } + } + } + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.ConformanceError{}, errs[0]) +} diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 96779bb70e..27e7e2f0cd 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -1997,150 +1997,533 @@ func TestCheckInvalidMultipleInterfaceDefaultImplementation(t *testing.T) { t.Parallel() - _, err := ParseAndCheck(t, ` - struct interface IA { - fun test(): Int { - return 41 + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + fun test(): Int { + return 41 + } + } + + struct interface IB { + fun test(): Int { + return 41 + } + } + + struct Test: IA, IB { + } - } - struct interface IB { fun test(): Int { - return 41 + return Test().test() } - } + `) - struct Test: IA, IB { + errs := ExpectCheckerErrors(t, err, 1) - } - `) + require.IsType(t, &sema.MultipleInterfaceDefaultImplementationsError{}, errs[0]) + }) - errs := ExpectCheckerErrors(t, err, 1) + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + fun test(): Int { + return 41 + } + } + } + + contract interface IB { + + struct X { + fun test(): Int { + return 41 + } + } + } + + contract Test: IA, IB { + + struct X {} + } - require.IsType(t, &sema.MultipleInterfaceDefaultImplementationsError{}, errs[0]) + fun test(): Int { + return Test.X().test() + } + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.MultipleInterfaceDefaultImplementationsError{}, errs[0]) + }) } func TestCheckMultipleInterfaceDefaultImplementationWhenOverriden(t *testing.T) { t.Parallel() - _, err := ParseAndCheck(t, ` - struct interface IA { - fun test(): Int{ - return 41 + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + fun test(): Int { + return 41 + } + } + + struct interface IB { + fun test(): Int { + return 41 + } + } + + struct Test: IA, IB { + fun test(): Int { + return 42 + } } - } - struct interface IB { fun test(): Int { - return 41 + return Test().test() + } + `) + + require.NoError(t, err) + }) + + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + fun test(): Int { + return 41 + } + } + } + + contract interface IB { + + struct X { + fun test(): Int { + return 41 + } + } + } + + contract Test: IA, IB { + + struct X { + fun test(): Int { + return 42 + } + } } - } - struct Test: IA, IB { fun test(): Int { - return 42 + return Test.X().test() } - } - `) + `) - require.NoError(t, err) + require.NoError(t, err) + }) } func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T) { t.Parallel() - _, err := ParseAndCheck(t, ` - struct interface IA { + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + fun test(): Int { + return 41 + } + } + + struct interface IB { + fun test(): Int + } + + struct Test: IA, IB { + + } + fun test(): Int { - return 41 + return Test().test() } - } + `) - struct interface IB { - fun test(): Int - } + errs := ExpectCheckerErrors(t, err, 1) - struct Test: IA, IB { + require.IsType(t, &sema.ConformanceError{}, errs[0]) + }) - } - `) + t.Run("type requirement", func(t *testing.T) { - errs := ExpectCheckerErrors(t, err, 1) + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + fun test(): Int { + return 41 + } + } + } + + contract interface IB { + struct X { + fun test(): Int + } + } + + contract Test: IA, IB { + struct X {} + } + + fun test(): Int { + return Test.X().test() + } + `) - require.IsType(t, &sema.ConformanceError{}, errs[0]) + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.ConformanceError{}, errs[0]) + }) } func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridden(t *testing.T) { t.Parallel() - _, err := ParseAndCheck(t, ` - struct interface IA { + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + fun test(): Int { + return 41 + } + } + + struct interface IB { + fun test(): Int + } + + struct Test: IA, IB { + fun test(): Int { + return 42 + } + } + fun test(): Int { - return 41 + return Test().test() } - } + `) + require.NoError(t, err) + }) - struct interface IB { - fun test(): Int - } + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + fun test(): Int { + return 41 + } + } + } + + contract interface IB { + + struct X { + fun test(): Int + } + } + + contract Test: IA, IB { + + struct X { + fun test(): Int { + return 42 + } + } + } - struct Test: IA, IB { fun test(): Int { - return 42 + return Test.X().test() } - } - `) - require.NoError(t, err) + `) + require.NoError(t, err) + }) +} + +func TestCheckInterfaceDefaultImplementation(t *testing.T) { + + t.Parallel() + + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + fun test(): Int { + return 42 + } + } + + struct Test: IA {} + + fun test(): Int { + return Test().test() + } + `) + require.NoError(t, err) + }) + + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + fun test(): Int { + return 42 + } + } + } + + contract Test: IA { + + struct X {} + } + + fun test(): Int { + return Test.X().test() + } + `) + require.NoError(t, err) + }) +} + +func TestCheckInterfaceDefaultImplementationOverriden(t *testing.T) { + + t.Parallel() + + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + fun test(): Int { + return 41 + } + } + + struct Test: IA { + fun test(): Int { + return 42 + } + } + + fun test(): Int { + return Test().test() + } + `) + require.NoError(t, err) + }) + + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + fun test(): Int { + return 41 + } + } + } + + contract Test: IA { + + struct X { + fun test(): Int { + return 42 + } + } + } + + fun test(): Int { + return Test.X().test() + } + `) + require.NoError(t, err) + }) } func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { t.Parallel() - _, err := ParseAndCheck(t, ` - struct interface IA { + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + fun test(): Int { + return self.x + } + } + + struct Test: IA { + let x: Int + + init() { + self.x = 0 + } + } + fun test(): Int { - return self.x + return Test().test() } - } + `) + + errs := ExpectCheckerErrors(t, err, 1) - struct Test: IA { - let x: Int + require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0]) + }) - init(x: Int) { - self.x = x + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + fun test(): Int { + return self.x + } + } } - } - `) - errs := ExpectCheckerErrors(t, err, 1) + contract Test: IA { + + struct X { + let x: Int + + init() { + self.x = 0 + } + } + } + + fun test(): Int { + return Test.X().test() + } + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0]) + }) - require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0]) } func TestCheckInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { t.Parallel() - _, err := ParseAndCheck(t, ` - struct interface IA { - let x: Int + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + let x: Int + + fun test(): Int { + return self.x + } + } + + struct Test: IA { + let x: Int + + init() { + self.x = 0 + } + } fun test(): Int { - return self.x + return Test().test() } - } + `) + require.NoError(t, err) + }) + + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() - struct Test: IA { - let x: Int + _, err := ParseAndCheck(t, ` + contract interface IA { - init(x: Int) { - self.x = x + struct X { + let x: Int + + fun test(): Int { + return self.x + } + } } - } - `) - require.NoError(t, err) + + contract Test: IA { + + struct X { + let x: Int + + init() { + self.x = 0 + } + } + } + + fun test(): Int { + return Test.X().test() + } + `) + require.NoError(t, err) + }) } diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 9d98339966..92c00d56ed 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -31,60 +31,151 @@ func TestInterpretInterfaceDefaultImplementation(t *testing.T) { t.Parallel() - inter := parseCheckAndInterpret(t, ` + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + + struct interface IA { + fun test(): Int { + return 42 + } + } + + struct Test: IA { - struct interface InterfaceA { - fun test(): Int { - return 42 } - } - struct Test: InterfaceA { + fun main(): Int { + return Test().test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewIntValueFromInt64(42), + value, + ) + }) + + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() - } + inter := parseCheckAndInterpret(t, ` - fun main(): Int { - return Test().test() - } - `) + contract interface IA { - value, err := inter.Invoke("main") - require.NoError(t, err) + struct X { + fun test(): Int { + return 42 + } + } + } + + contract Test: IA { + struct X { + } + } + + fun main(): Int { + return Test.X().test() + } + `) - assert.Equal(t, - interpreter.NewIntValueFromInt64(42), - value, - ) + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewIntValueFromInt64(42), + value, + ) + }) } func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { t.Parallel() - inter := parseCheckAndInterpret(t, ` + t.Run("interface", func(t *testing.T) { - struct interface InterfaceA { - fun test(): Int { - return 41 - } - } + t.Parallel() + + inter := parseCheckAndInterpret(t, ` - struct Test: InterfaceA { - fun test(): Int { - return 42 + struct interface IA { + fun test(): Int { + return 41 + } } - } - fun main(): Int { - return Test().test() - } - `) + struct Test: IA { + fun test(): Int { + return 42 + } + } - value, err := inter.Invoke("main") - require.NoError(t, err) + fun main(): Int { + return Test().test() + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewIntValueFromInt64(42), + value, + ) + }) + + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + inter, err := parseCheckAndInterpretWithOptions(t, + ` + contract interface IA { + + struct X { + fun test(): Int { + return 41 + } + } + } + + contract Test: IA { + + struct X { + fun test(): Int { + return 42 + } + } + } + + fun main(): Int { + return Test.X().test() + } + `, + ParseCheckAndInterpretOptions{ + Options: []interpreter.Option{ + makeContractValueHandler(nil, nil, nil), + }, + }, + ) + + require.NoError(t, err) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewIntValueFromInt64(42), + value, + ) + }) - assert.Equal(t, - interpreter.NewIntValueFromInt64(42), - value, - ) } From 25ddfe4f7cdcadbf33cd9e01956be3b1a7ab82bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 16:16:38 -0800 Subject: [PATCH 26/43] fix checking for multipel type requirements with default implementations --- runtime/sema/check_composite_declaration.go | 35 +++++++++++++++------ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index f139eb8b31..0ac7a08336 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -158,7 +158,8 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl checkMissingMembers := kind != ContainerKindInterface - inherited := map[string]struct{}{} + inheritedMembers := map[string]struct{}{} + typeRequirementsInheritedMembers := map[string]map[string]struct{}{} for i, interfaceType := range compositeType.ExplicitInterfaceConformances { interfaceNominalType := declaration.Conformances[i] @@ -172,7 +173,8 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl checkMissingMembers: checkMissingMembers, interfaceTypeIsTypeRequirement: false, }, - inherited, + inheritedMembers, + typeRequirementsInheritedMembers, ) } @@ -970,16 +972,23 @@ type compositeConformanceCheckOptions struct { // checkCompositeConformance checks if the given composite declaration with the given composite type // conforms to the specified interface type. // -// inherited is an "input/output parameter": It tracks which member were inherited from the interface. +// inheritedMembers is an "input/output parameter": +// It tracks which members were inherited from the interface. // It allows tracking this across conformance checks of multiple interfaces. // +// typeRequirementsInheritedMembers is an "input/output parameter": +// It tracks which members were inherited in each nested type, which may be a conformance to a type requirement. +// It allows tracking this across conformance checks of multiple interfaces' type requirements. +// func (checker *Checker) checkCompositeConformance( compositeDeclaration *ast.CompositeDeclaration, compositeType *CompositeType, interfaceType *InterfaceType, compositeKindMismatchIdentifier ast.Identifier, options compositeConformanceCheckOptions, - inherited map[string]struct{}, + inheritedMembers map[string]struct{}, + // type requirement name -> inherited members + typeRequirementsInheritedMembers map[string]map[string]struct{}, ) { var missingMembers []*Member @@ -1041,7 +1050,8 @@ func (checker *Checker) checkCompositeConformance( // If the composite member exists, check if it satisfies the mem if compositeMember != nil && !checker.memberSatisfied(compositeMember, interfaceMember) { - memberMismatches = append(memberMismatches, + memberMismatches = append( + memberMismatches, MemberMismatch{ CompositeMember: compositeMember, InterfaceMember: interfaceMember, @@ -1058,7 +1068,7 @@ func (checker *Checker) checkCompositeConformance( if interfaceMember.DeclarationKind == common.DeclarationKindFunction && interfaceMember.HasImplementation { - if _, ok := inherited[name]; ok { + if _, ok := inheritedMembers[name]; ok { checker.report( &MultipleInterfaceDefaultImplementationsError{ CompositeType: compositeType, @@ -1066,7 +1076,7 @@ func (checker *Checker) checkCompositeConformance( }, ) } - inherited[name] = struct{}{} + inheritedMembers[name] = struct{}{} } else { missingMembers = append(missingMembers, interfaceMember) @@ -1093,7 +1103,13 @@ func (checker *Checker) checkCompositeConformance( return } - checker.checkTypeRequirement(nestedCompositeType, compositeDeclaration, requiredCompositeType) + inherited, ok := typeRequirementsInheritedMembers[name] + if inherited == nil { + inherited = map[string]struct{}{} + typeRequirementsInheritedMembers[name] = inherited + } + + checker.checkTypeRequirement(nestedCompositeType, compositeDeclaration, requiredCompositeType, inherited) }) if len(missingMembers) > 0 || @@ -1221,6 +1237,7 @@ func (checker *Checker) checkTypeRequirement( declaredType Type, containerDeclaration *ast.CompositeDeclaration, requiredCompositeType *CompositeType, + inherited map[string]struct{}, ) { // A nested interface doesn't satisfy the type requirement, @@ -1325,7 +1342,6 @@ func (checker *Checker) checkTypeRequirement( // like a top-level composite declaration to an interface type requiredInterfaceType := requiredCompositeType.InterfaceType() - inherited := map[string]struct{}{} checker.checkCompositeConformance( compositeDeclaration, @@ -1337,6 +1353,7 @@ func (checker *Checker) checkTypeRequirement( interfaceTypeIsTypeRequirement: true, }, inherited, + nil, ) } From acdb83eb5073126d4602bdadc36c44aa52bf6eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 18:56:42 -0800 Subject: [PATCH 27/43] simplify --- runtime/sema/check_composite_declaration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 0ac7a08336..69dc5947fe 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1049,7 +1049,7 @@ func (checker *Checker) checkCompositeConformance( // If the composite member exists, check if it satisfies the mem - if compositeMember != nil && !checker.memberSatisfied(compositeMember, interfaceMember) { + if !checker.memberSatisfied(compositeMember, interfaceMember) { memberMismatches = append( memberMismatches, MemberMismatch{ From e5d400f79b724b20d7a82518d7e8c6dd09af30b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 18:56:59 -0800 Subject: [PATCH 28/43] fix test, provide contract handler --- runtime/tests/interpreter/interface_test.go | 32 ++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 92c00d56ed..4f35584d1e 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -65,26 +65,32 @@ func TestInterpretInterfaceDefaultImplementation(t *testing.T) { t.Parallel() - inter := parseCheckAndInterpret(t, ` + inter, err := parseCheckAndInterpretWithOptions(t, ` - contract interface IA { + contract interface IA { - struct X { - fun test(): Int { - return 42 + struct X { + fun test(): Int { + return 42 + } } } - } - contract Test: IA { - struct X { + contract Test: IA { + struct X { + } } - } - fun main(): Int { - return Test.X().test() - } - `) + fun main(): Int { + return Test.X().test() + } + `, + ParseCheckAndInterpretOptions{ + Options: []interpreter.Option{ + makeContractValueHandler(nil, nil, nil), + }, + }, + ) value, err := inter.Invoke("main") require.NoError(t, err) From 7fade15947bd8d0e8bf8e17737b0c41db572ec5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 17 Jan 2022 18:57:17 -0800 Subject: [PATCH 29/43] declare default functions of type requirement --- runtime/sema/type.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 777dbbe7fa..4c41ae42b0 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3462,6 +3462,27 @@ func (t *CompositeType) initializeExplicitInterfaceConformanceSet() { func (t *CompositeType) addImplicitTypeRequirementConformance(typeRequirement *CompositeType) { t.ImplicitTypeRequirementConformances = append(t.ImplicitTypeRequirementConformances, typeRequirement) + + // Add default functions + + typeRequirement.Members.Foreach(func(memberName string, member *Member) { + if member.Predeclared || + member.DeclarationKind != common.DeclarationKindFunction || + !member.HasImplementation { + + return + } + + _, existing := t.Members.Get(memberName) + if existing { + return + } + + inheritedMember := *member + inheritedMember.ContainerType = t + + t.Members.Set(memberName, &inheritedMember) + }) } func (*CompositeType) IsType() {} From d2c6bb8957be6bf3a02dfd8a2dec4177fe6cc93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 18 Jan 2022 15:26:02 -0800 Subject: [PATCH 30/43] fix checking of default functions in type requirements --- runtime/sema/check_composite_declaration.go | 83 ++++++++++++++++++--- runtime/sema/errors.go | 26 +++++++ runtime/sema/type.go | 21 ------ runtime/tests/checker/interface_test.go | 4 +- 4 files changed, 100 insertions(+), 34 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 69dc5947fe..b4c6d5d13f 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -582,11 +582,14 @@ func (checker *Checker) declareCompositeMembersAndValue( // and this nested composite type implicitly conforms to it. compositeType.GetNestedTypes().Foreach(func(nestedTypeIdentifier string, nestedType Type) { + nestedCompositeType, ok := nestedType.(*CompositeType) if !ok { return } + inheritedMembers := map[string]*Member{} + for _, compositeTypeConformance := range compositeType.ExplicitInterfaceConformances { conformanceNestedTypes := compositeTypeConformance.GetNestedTypes() @@ -601,6 +604,52 @@ func (checker *Checker) declareCompositeMembersAndValue( } nestedCompositeType.addImplicitTypeRequirementConformance(typeRequirement) + + // Add default functions + + typeRequirement.Members.Foreach(func(memberName string, member *Member) { + + if member.Predeclared || + member.DeclarationKind != common.DeclarationKindFunction { + + return + } + + _, existing := nestedCompositeType.Members.Get(memberName) + if existing { + return + } + + if _, ok := inheritedMembers[memberName]; ok { + if member.HasImplementation { + checker.report( + &MultipleInterfaceDefaultImplementationsError{ + CompositeType: nestedCompositeType, + Member: member, + }, + ) + } else { + checker.report( + &DefaultFunctionConflictError{ + CompositeType: nestedCompositeType, + Member: member, + }, + ) + } + + return + } + + if member.HasImplementation { + inheritedMembers[memberName] = member + } + }) + } + + for memberName, member := range inheritedMembers { + inheritedMember := *member + inheritedMember.ContainerType = nestedCompositeType + nestedCompositeType.Members.Set(memberName, &inheritedMember) } }) @@ -1065,22 +1114,34 @@ func (checker *Checker) checkCompositeConformance( // However, only one of the composite's conformances (interfaces) // may provide a default function. - if interfaceMember.DeclarationKind == common.DeclarationKindFunction && - interfaceMember.HasImplementation { + if interfaceMember.DeclarationKind == common.DeclarationKindFunction { if _, ok := inheritedMembers[name]; ok { - checker.report( - &MultipleInterfaceDefaultImplementationsError{ - CompositeType: compositeType, - Member: interfaceMember, - }, - ) + if interfaceMember.HasImplementation { + checker.report( + &MultipleInterfaceDefaultImplementationsError{ + CompositeType: compositeType, + Member: interfaceMember, + }, + ) + } else { + checker.report( + &DefaultFunctionConflictError{ + CompositeType: compositeType, + Member: interfaceMember, + }, + ) + } + return } - inheritedMembers[name] = struct{}{} - } else { - missingMembers = append(missingMembers, interfaceMember) + if interfaceMember.HasImplementation { + inheritedMembers[name] = struct{}{} + return + } } + + missingMembers = append(missingMembers, interfaceMember) } }) diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 0a11c5850b..8cbcf3cf4c 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1038,6 +1038,32 @@ func (e *MultipleInterfaceDefaultImplementationsError) EndPosition() ast.Positio func (*MultipleInterfaceDefaultImplementationsError) isSemanticError() {} +// DefaultFunctionConflictError +// +type DefaultFunctionConflictError struct { + CompositeType *CompositeType + Member *Member +} + +func (e *DefaultFunctionConflictError) Error() string { + return fmt.Sprintf( + "%s `%s` has conflicting requirements for function `%s`", + e.CompositeType.Kind.Name(), + e.CompositeType.QualifiedString(), + e.Member.Identifier.Identifier, + ) +} + +func (e *DefaultFunctionConflictError) StartPosition() ast.Position { + return e.Member.Identifier.StartPosition() +} + +func (e *DefaultFunctionConflictError) EndPosition() ast.Position { + return e.Member.Identifier.EndPosition() +} + +func (*DefaultFunctionConflictError) isSemanticError() {} + // MissingConformanceError type MissingConformanceError struct { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 4c41ae42b0..777dbbe7fa 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3462,27 +3462,6 @@ func (t *CompositeType) initializeExplicitInterfaceConformanceSet() { func (t *CompositeType) addImplicitTypeRequirementConformance(typeRequirement *CompositeType) { t.ImplicitTypeRequirementConformances = append(t.ImplicitTypeRequirementConformances, typeRequirement) - - // Add default functions - - typeRequirement.Members.Foreach(func(memberName string, member *Member) { - if member.Predeclared || - member.DeclarationKind != common.DeclarationKindFunction || - !member.HasImplementation { - - return - } - - _, existing := t.Members.Get(memberName) - if existing { - return - } - - inheritedMember := *member - inheritedMember.ContainerType = t - - t.Members.Set(memberName, &inheritedMember) - }) } func (*CompositeType) IsType() {} diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 27e7e2f0cd..cb4e5ccf24 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2173,7 +2173,7 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T errs := ExpectCheckerErrors(t, err, 1) - require.IsType(t, &sema.ConformanceError{}, errs[0]) + require.IsType(t, &sema.DefaultFunctionConflictError{}, errs[0]) }) t.Run("type requirement", func(t *testing.T) { @@ -2207,7 +2207,7 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T errs := ExpectCheckerErrors(t, err, 1) - require.IsType(t, &sema.ConformanceError{}, errs[0]) + require.IsType(t, &sema.DefaultFunctionConflictError{}, errs[0]) }) } From 968d06d7dcb77adefd2482bcdb2eb2d72f0c0c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 18 Jan 2022 16:06:48 -0800 Subject: [PATCH 31/43] link in type requirement default functions --- runtime/interpreter/interpreter.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 0515bc3797..c55247597c 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -1897,12 +1897,12 @@ func (interpreter *Interpreter) compositeDestructorFunction( } } -func (interpreter *Interpreter) interfaceFunctions( - interfaceDeclaration *ast.InterfaceDeclaration, +func (interpreter *Interpreter) defaultFunctions( + members *ast.Members, lexicalScope *VariableActivation, ) map[string]FunctionValue { - functionDeclarations := interfaceDeclaration.Members.Functions() + functionDeclarations := members.Functions() functionCount := len(functionDeclarations) var functions map[string]FunctionValue @@ -2296,13 +2296,13 @@ func (interpreter *Interpreter) declareInterface( initializerFunctionWrapper := interpreter.initializerFunctionWrapper(declaration.Members, lexicalScope) destructorFunctionWrapper := interpreter.destructorFunctionWrapper(declaration.Members, lexicalScope) functionWrappers := interpreter.functionWrappers(declaration.Members, lexicalScope) - interfaceFunctions := interpreter.interfaceFunctions(declaration, lexicalScope) + defaultFunctions := interpreter.defaultFunctions(declaration.Members, lexicalScope) interpreter.typeCodes.InterfaceCodes[typeID] = WrapperCode{ InitializerFunctionWrapper: initializerFunctionWrapper, DestructorFunctionWrapper: destructorFunctionWrapper, FunctionWrappers: functionWrappers, - Functions: interfaceFunctions, + Functions: defaultFunctions, } } @@ -2332,11 +2332,13 @@ func (interpreter *Interpreter) declareTypeRequirement( initializerFunctionWrapper := interpreter.initializerFunctionWrapper(declaration.Members, lexicalScope) destructorFunctionWrapper := interpreter.destructorFunctionWrapper(declaration.Members, lexicalScope) functionWrappers := interpreter.functionWrappers(declaration.Members, lexicalScope) + defaultFunctions := interpreter.defaultFunctions(declaration.Members, lexicalScope) interpreter.typeCodes.TypeRequirementCodes[typeID] = WrapperCode{ InitializerFunctionWrapper: initializerFunctionWrapper, DestructorFunctionWrapper: destructorFunctionWrapper, FunctionWrappers: functionWrappers, + Functions: defaultFunctions, } } From 6e0c4dba30ba6018e79b1daf984755aba3c49ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 18 Jan 2022 16:22:19 -0800 Subject: [PATCH 32/43] lint --- runtime/sema/check_composite_declaration.go | 12 ++++++------ runtime/tests/interpreter/interface_test.go | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index b4c6d5d13f..ed706c4408 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -588,7 +588,7 @@ func (checker *Checker) declareCompositeMembersAndValue( return } - inheritedMembers := map[string]*Member{} + inheritedMembers := NewStringMemberOrderedMap() for _, compositeTypeConformance := range compositeType.ExplicitInterfaceConformances { conformanceNestedTypes := compositeTypeConformance.GetNestedTypes() @@ -620,7 +620,7 @@ func (checker *Checker) declareCompositeMembersAndValue( return } - if _, ok := inheritedMembers[memberName]; ok { + if _, ok := inheritedMembers.Get(memberName); ok { if member.HasImplementation { checker.report( &MultipleInterfaceDefaultImplementationsError{ @@ -641,16 +641,16 @@ func (checker *Checker) declareCompositeMembersAndValue( } if member.HasImplementation { - inheritedMembers[memberName] = member + inheritedMembers.Set(memberName, member) } }) } - for memberName, member := range inheritedMembers { + inheritedMembers.Foreach(func(memberName string, member *Member) { inheritedMember := *member inheritedMember.ContainerType = nestedCompositeType nestedCompositeType.Members.Set(memberName, &inheritedMember) - } + }) }) // Declare members @@ -1164,7 +1164,7 @@ func (checker *Checker) checkCompositeConformance( return } - inherited, ok := typeRequirementsInheritedMembers[name] + inherited := typeRequirementsInheritedMembers[name] if inherited == nil { inherited = map[string]struct{}{} typeRequirementsInheritedMembers[name] = inherited diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 4f35584d1e..2411cc5674 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -91,6 +91,7 @@ func TestInterpretInterfaceDefaultImplementation(t *testing.T) { }, }, ) + require.NoError(t, err) value, err := inter.Invoke("main") require.NoError(t, err) From 8b2d36e15f18309f060247c0a996d822ada55801 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Tue, 26 Apr 2022 12:19:59 +0200 Subject: [PATCH 33/43] Update runtime/interpreter/interpreter.go Co-authored-by: Supun Setunga --- runtime/interpreter/interpreter.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 98035b1368..0c50a3d83b 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -1905,10 +1905,11 @@ func (interpreter *Interpreter) defaultFunctions( functionDeclarations := members.Functions() functionCount := len(functionDeclarations) - var functions map[string]FunctionValue - if functionCount > 0 { - functions = make(map[string]FunctionValue, functionCount) + if functionCount == 0 { + return nil } + + functions := make(map[string]FunctionValue, functionCount) for _, functionDeclaration := range functionDeclarations { name := functionDeclaration.Identifier.Identifier From cd49c019bbaa1684dd786e5d73a0283e7a900f10 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Wed, 27 Apr 2022 17:47:04 +0200 Subject: [PATCH 34/43] Error on implementation on special functions on interfaces --- runtime/sema/check_interface_declaration.go | 11 ++++++++++ runtime/sema/errors.go | 23 +++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 6bebab9dfd..0739727e3d 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -79,6 +79,17 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl checker.checkUnknownSpecialFunctions(declaration.Members.SpecialFunctions()) + for _, specialFunction := range declaration.Members.SpecialFunctions() { + if specialFunction.FunctionDeclaration.FunctionBlock.HasStatements() { + checker.report( + &SpecialFunctionDefaultImplementationError{ + Identifier: specialFunction.DeclarationIdentifier(), + Container: declaration, + }, + ) + } + } + checker.checkInterfaceFunctions( declaration.Members.Functions(), interfaceType, diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 5da7ef1819..3602ab7dc0 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1059,6 +1059,29 @@ func (e *MultipleInterfaceDefaultImplementationsError) EndPosition() ast.Positio func (*MultipleInterfaceDefaultImplementationsError) isSemanticError() {} +//SpecialFunctionDefaultImplementationError +// +type SpecialFunctionDefaultImplementationError struct { + Container *ast.InterfaceDeclaration + Identifier *ast.Identifier +} + +func (e *SpecialFunctionDefaultImplementationError) Error() string { + return fmt.Sprintf( + "%s may not be defined as a default function on interface %s", + e.Identifier.Identifier, + e.Container.Identifier.Identifier, + ) +} + +func (e *SpecialFunctionDefaultImplementationError) StartPosition() ast.Position { + return e.Identifier.StartPosition() +} + +func (e *SpecialFunctionDefaultImplementationError) EndPosition() ast.Position { + return e.Identifier.EndPosition() +} + // DefaultFunctionConflictError // type DefaultFunctionConflictError struct { From 6c92d06d9a6261797a0cf2edbd142bdb7f3cce78 Mon Sep 17 00:00:00 2001 From: Deniz Mert Edincik Date: Wed, 27 Apr 2022 17:52:28 +0200 Subject: [PATCH 35/43] test for SpecialFunctionDefaultImplementationError --- runtime/tests/checker/interface_test.go | 49 ++++++++++++------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 43f16c2cfd..440e036cd1 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2391,66 +2391,65 @@ func TestCheckInterfaceDefaultImplementationOverriden(t *testing.T) { }) } -func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { +func TestSpecialFunctionDefaultImplementationUsage(t *testing.T) { t.Parallel() - t.Run("interface", func(t *testing.T) { + t.Run("special", func(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` struct interface IA { - fun test(): Int { - return self.x - } + var x: Int + init(){ + self.x = 1 + } } struct Test: IA { - let x: Int + var x: Int init() { self.x = 0 } } - fun test(): Int { - return Test().test() - } + `) errs := ExpectCheckerErrors(t, err, 1) - require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0]) + require.IsType(t, &sema.SpecialFunctionDefaultImplementationError{}, errs[0]) }) - t.Run("type requirement", func(t *testing.T) { +} + +func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { + + t.Parallel() + + t.Run("interface", func(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` - contract interface IA { - - struct X { - fun test(): Int { - return self.x - } + struct interface IA { + fun test(): Int { + return self.x } } - contract Test: IA { - - struct X { - let x: Int + struct Test: IA { + let x: Int - init() { - self.x = 0 - } + init() { + self.x = 0 } } fun test(): Int { - return Test.X().test() + return Test().test() } `) From 8327e9d6b777fdb96522f13c9599077a576749b5 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 20 Jun 2022 11:33:26 -0700 Subject: [PATCH 36/43] Sync with master --- runtime/sema/check_composite_declaration.go | 2 +- runtime/sema/errors.go | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 87fd8401e1..5a28bebac7 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1416,7 +1416,7 @@ func (checker *Checker) checkTypeRequirement( interfaceTypeIsTypeRequirement: true, }, inherited, - nil, + map[string]map[string]struct{}{}, ) } diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index d93e0d4124..082a330fd3 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1060,8 +1060,8 @@ func (e *MultipleInterfaceDefaultImplementationsError) StartPosition() ast.Posit return e.Member.Identifier.StartPosition() } -func (e *MultipleInterfaceDefaultImplementationsError) EndPosition() ast.Position { - return e.Member.Identifier.EndPosition() +func (e *MultipleInterfaceDefaultImplementationsError) EndPosition(memoryGauge common.MemoryGauge) ast.Position { + return e.Member.Identifier.EndPosition(memoryGauge) } func (*MultipleInterfaceDefaultImplementationsError) isSemanticError() {} @@ -1085,8 +1085,8 @@ func (e *SpecialFunctionDefaultImplementationError) StartPosition() ast.Position return e.Identifier.StartPosition() } -func (e *SpecialFunctionDefaultImplementationError) EndPosition() ast.Position { - return e.Identifier.EndPosition() +func (e *SpecialFunctionDefaultImplementationError) EndPosition(memoryGauge common.MemoryGauge) ast.Position { + return e.Identifier.EndPosition(memoryGauge) } // DefaultFunctionConflictError @@ -1109,8 +1109,8 @@ func (e *DefaultFunctionConflictError) StartPosition() ast.Position { return e.Member.Identifier.StartPosition() } -func (e *DefaultFunctionConflictError) EndPosition() ast.Position { - return e.Member.Identifier.EndPosition() +func (e *DefaultFunctionConflictError) EndPosition(memoryGauge common.MemoryGauge) ast.Position { + return e.Member.Identifier.EndPosition(memoryGauge) } func (*DefaultFunctionConflictError) isSemanticError() {} From eb899b0fca520c7aa93823ed54631a5ec63cd823 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 20 Jun 2022 13:00:03 -0700 Subject: [PATCH 37/43] Add testcase for interface variable interpreting --- runtime/interpreter/interpreter.go | 15 +++--- runtime/tests/interpreter/interface_test.go | 55 +++++++++++++++++++++ 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 9747a8d094..f9ca9cde6a 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -2056,18 +2056,19 @@ func (interpreter *Interpreter) defaultFunctions( if functionCount == 0 { return nil } - + functions := make(map[string]FunctionValue, functionCount) for _, functionDeclaration := range functionDeclarations { name := functionDeclaration.Identifier.Identifier - if functionDeclaration.FunctionBlock.HasStatements() { - functions[name] = - interpreter.compositeFunction( - functionDeclaration, - lexicalScope, - ) + if !functionDeclaration.FunctionBlock.HasStatements() { + continue } + + functions[name] = interpreter.compositeFunction( + functionDeclaration, + lexicalScope, + ) } return functions diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 6db606a3d6..88f0dc2900 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -101,6 +101,61 @@ func TestInterpretInterfaceDefaultImplementation(t *testing.T) { value, ) }) + + t.Run("interface variable", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct interface IA { + let x: Int + fun getX(): Int { + return self.x + } + } + + struct Foo: IA { + let x: Int + init() { + self.x = 123 + } + } + + struct Bar: IA { + let x: Int + init() { + self.x = 456 + } + } + + fun test(): [Int;2] { + let foo = Foo() + let bar = Bar() + + return [foo.getX(), bar.getX()] + } + `) + + value, err := inter.Invoke("test") + require.NoError(t, err) + + require.IsType(t, &interpreter.ArrayValue{}, value) + array := value.(*interpreter.ArrayValue) + + // Check here whether: + // - The value set for `x` by the implementation is correctly set/returned. + // - Correct variable scope is used / Scopes are not shared. + // i.e: Value set by `Foo` doesn't affect `Bar`, and vice-versa + + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(123), + array.Get(inter, nil, 0), + ) + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(456), + array.Get(inter, nil, 1), + ) + }) } func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { From bcf231a7f666e9551275d8aba5f0fa13b5507633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 15 Aug 2022 13:12:35 -0700 Subject: [PATCH 38/43] switch to new generic ordered map --- runtime/sema/check_composite_declaration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index b88b0f8ac9..0f2ee87ab4 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -588,7 +588,7 @@ func (checker *Checker) declareCompositeMembersAndValue( return } - inheritedMembers := NewStringMemberOrderedMap() + var inheritedMembers StringMemberOrderedMap for _, compositeTypeConformance := range compositeType.ExplicitInterfaceConformances { conformanceNestedTypes := compositeTypeConformance.GetNestedTypes() From 3f9e74da3727137806ff01eb03fc2811ee4c9542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 15 Aug 2022 13:15:03 -0700 Subject: [PATCH 39/43] clean up --- runtime/tests/checker/interface_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index d4bec99377..3960bd76b7 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2405,10 +2405,10 @@ func TestSpecialFunctionDefaultImplementationUsage(t *testing.T) { _, err := ParseAndCheck(t, ` struct interface IA { - var x: Int + var x: Int init(){ - self.x = 1 - } + self.x = 1 + } } struct Test: IA { From b1ad20e2cb50c0f500b52e5fe110298a2b51bcec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 15 Aug 2022 13:38:58 -0700 Subject: [PATCH 40/43] check default special functions in type requirements --- runtime/sema/check_composite_declaration.go | 18 +++++ runtime/sema/check_interface_declaration.go | 15 +--- runtime/sema/errors.go | 8 +- runtime/tests/checker/interface_test.go | 82 +++++++++++++++++++-- 4 files changed, 103 insertions(+), 20 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 0f2ee87ab4..61a5dc6354 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -119,6 +119,8 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl ) case ContainerKindInterface: + checker.checkSpecialFunctionDefaultImplementation(declaration, "type requirement") + checker.checkInterfaceFunctions( declaration.Members.Functions(), compositeType, @@ -2052,6 +2054,22 @@ func (checker *Checker) checkUnknownSpecialFunctions(functions []*ast.SpecialFun } } +func (checker *Checker) checkSpecialFunctionDefaultImplementation(declaration ast.Declaration, kindName string) { + for _, specialFunction := range declaration.DeclarationMembers().SpecialFunctions() { + if !specialFunction.FunctionDeclaration.FunctionBlock.HasStatements() { + continue + } + + checker.report( + &SpecialFunctionDefaultImplementationError{ + Identifier: specialFunction.DeclarationIdentifier(), + Container: declaration, + KindName: kindName, + }, + ) + } +} + func (checker *Checker) checkDestructors( destructors []*ast.SpecialFunctionDeclaration, fields map[string]*ast.FieldDeclaration, diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 02c78557c3..6901b185eb 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -78,17 +78,10 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl ) checker.checkUnknownSpecialFunctions(declaration.Members.SpecialFunctions()) - - for _, specialFunction := range declaration.Members.SpecialFunctions() { - if specialFunction.FunctionDeclaration.FunctionBlock.HasStatements() { - checker.report( - &SpecialFunctionDefaultImplementationError{ - Identifier: specialFunction.DeclarationIdentifier(), - Container: declaration, - }, - ) - } - } + checker.checkSpecialFunctionDefaultImplementation( + declaration, + declaration.DeclarationKind().Name(), + ) checker.checkInterfaceFunctions( declaration.Members.Functions(), diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 3c172cf459..dc1c17227c 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1280,8 +1280,9 @@ func (e *MultipleInterfaceDefaultImplementationsError) EndPosition(memoryGauge c // SpecialFunctionDefaultImplementationError // type SpecialFunctionDefaultImplementationError struct { - Container *ast.InterfaceDeclaration + Container ast.Declaration Identifier *ast.Identifier + KindName string } var _ SemanticError = &SpecialFunctionDefaultImplementationError{} @@ -1293,9 +1294,10 @@ func (*SpecialFunctionDefaultImplementationError) IsUserError() {} func (e *SpecialFunctionDefaultImplementationError) Error() string { return fmt.Sprintf( - "%s may not be defined as a default function on interface %s", + "%s may not be defined as a default function on %s %s", e.Identifier.Identifier, - e.Container.Identifier.Identifier, + e.KindName, + e.Container.DeclarationIdentifier().Identifier, ) } diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 3960bd76b7..57fd62eba4 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -1950,7 +1950,6 @@ func TestCheckContractInterfaceFungibleTokenUse(t *testing.T) { // TestCheckInvalidInterfaceUseAsTypeSuggestion tests that an interface // can not be used as a type, and the suggestion to fix it is correct -// func TestCheckInvalidInterfaceUseAsTypeSuggestion(t *testing.T) { t.Parallel() @@ -2399,15 +2398,16 @@ func TestSpecialFunctionDefaultImplementationUsage(t *testing.T) { t.Parallel() - t.Run("special", func(t *testing.T) { + t.Run("interface", func(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` struct interface IA { var x: Int - init(){ - self.x = 1 + + init() { + self.x = 1 } } @@ -2418,8 +2418,6 @@ func TestSpecialFunctionDefaultImplementationUsage(t *testing.T) { self.x = 0 } } - - `) errs := ExpectCheckerErrors(t, err, 1) @@ -2427,6 +2425,42 @@ func TestSpecialFunctionDefaultImplementationUsage(t *testing.T) { require.IsType(t, &sema.SpecialFunctionDefaultImplementationError{}, errs[0]) }) + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + var x: Int + + init() { + self.x = 1 + } + } + } + + contract Test: IA { + + struct X { + var x: Int + + init() { + self.x = 0 + } + } + } + + fun test() { + Test.X() + } + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.SpecialFunctionDefaultImplementationError{}, errs[0]) + }) } func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { @@ -2462,6 +2496,42 @@ func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage(t *testing. require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0]) }) + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + fun test(): Int { + return self.x + } + } + } + + contract Test: IA { + + struct X { + let x: Int + + init() { + self.x = 0 + } + } + } + + fun test() { + Test.X() + } + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0]) + }) +} + } func TestCheckInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { From 2d29b4288037ed597b25023c1590663d166ad558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 15 Aug 2022 13:46:32 -0700 Subject: [PATCH 41/43] check assignment to effeciviely constant field in default function --- runtime/sema/check_assignment.go | 2 +- runtime/tests/checker/interface_test.go | 71 +++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/runtime/sema/check_assignment.go b/runtime/sema/check_assignment.go index 7afe1cc5f1..b0bc70c5e9 100644 --- a/runtime/sema/check_assignment.go +++ b/runtime/sema/check_assignment.go @@ -274,7 +274,7 @@ func (checker *Checker) visitMemberExpressionAssignment( ) } - targetIsConstant := member.VariableKind == ast.VariableKindConstant + targetIsConstant := member.VariableKind != ast.VariableKindVariable // If this is an assignment to a `self` field, it needs special handling // depending on if the assignment is in an initializer or not diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 57fd62eba4..35eb8ea9e1 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -2532,6 +2532,77 @@ func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage(t *testing. }) } +func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage2(t *testing.T) { + + t.Parallel() + + t.Run("interface", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + struct interface IA { + x: Int + + fun test() { + self.x = 1 + } + } + + struct Test: IA { + let x: Int + + init() { + self.x = 0 + } + } + + fun test() { + Test().test() + } + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.AssignmentToConstantMemberError{}, errs[0]) + }) + + t.Run("type requirement", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + contract interface IA { + + struct X { + x: Int + + fun test() { + self.x = 1 + } + } + } + + contract Test: IA { + + struct X { + let x: Int + + init() { + self.x = 0 + } + } + } + + fun test() { + Test.X() + } + `) + + errs := ExpectCheckerErrors(t, err, 1) + + require.IsType(t, &sema.AssignmentToConstantMemberError{}, errs[0]) + }) } func TestCheckInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { From 94cea1cdedf98a484b14a4149bc46af6a96e313d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 16 Aug 2022 13:46:33 -0700 Subject: [PATCH 42/43] simplify --- runtime/sema/check_composite_declaration.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 61a5dc6354..300c4f21e1 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -1878,16 +1878,14 @@ func (checker *Checker) checkSpecialFunction( checkResourceLoss, ) - switch containerKind { - - case ContainerKindComposite: - // Event declarations have an empty initializer as it is synthesized - + if containerKind == ContainerKindComposite { compositeType, ok := containerType.(*CompositeType) if !ok { // we just checked that the container was a composite panic(errors.NewUnreachableError()) } + + // Event declarations have an empty initializer as it is synthesized if compositeType.Kind != common.CompositeKindEvent && specialFunction.FunctionDeclaration.FunctionBlock == nil { From dffc1202f93a9fb2cc793cc7a5f0c12820db7adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 16 Aug 2022 13:58:19 -0700 Subject: [PATCH 43/43] document interface default functions --- docs/language/interfaces.mdx | 43 +++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/language/interfaces.mdx b/docs/language/interfaces.mdx index 980cb230cd..c5faee1b3b 100644 --- a/docs/language/interfaces.mdx +++ b/docs/language/interfaces.mdx @@ -455,7 +455,7 @@ resource interface OuterInterface { struct interface InnerInterface {} } -// Declare a resource named `SomeOuter` that implements the interface `OuterInterface` +// Declare a resource named `SomeOuter` that implements the interface `OuterInterface`. // // The resource is not required to implement `OuterInterface.InnerInterface`. // @@ -467,3 +467,44 @@ resource SomeOuter: OuterInterface {} struct SomeInner: OuterInterface.InnerInterface {} ``` + +## Interface Default Functions + +Interfaces can provide default functions: +If the concrete type implementing the interface does not provide an implementation +for the function required by the interface, +then the interface's default function is used in the implementation. + +```cadence +// Declare a struct interface `Container`, +// which declares a default function `getCount`. +// +struct interface Container { + + let items: [AnyStruct] + + fun getCount(): Int { + return self.items.length + } +} + +// Declare a concrete struct named `Numbers` that implements the interface `Container`. +// +// The struct does not implement the function `getCount` of the interface `Container`, +// so the default function for `getCount` is used. +// +struct Numbers: Container { + let items: [AnyStruct] + + init() { + self.items = [] + } +} + +let numbers = Numbers() +numbers.getCount() // is 0 +``` + +Interfaces cannot provide default initializers or default destructors. + +Only one conformance may provide a default function.