Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Interface default implementation support #1076

Merged
merged 51 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
0cbf493
Interface default implementation support
bluesign Jul 16, 2021
a1ad121
Update runtime/tests/checker/interface_test.go
bluesign Nov 25, 2021
366448b
Update runtime/tests/interpreter/interpreter_test.go
bluesign Nov 25, 2021
fb45f52
Update runtime/tests/interpreter/interpreter_test.go
bluesign Nov 25, 2021
ffb612c
Update runtime/tests/checker/interface_test.go
bluesign Nov 25, 2021
2edcd7f
tabs to spaces in code literals
bluesign Nov 25, 2021
df52dd2
Update runtime/tests/checker/interface_test.go
bluesign Nov 25, 2021
195387f
Update runtime/tests/checker/interface_test.go
bluesign Nov 25, 2021
a1c8086
Update runtime/tests/checker/interface_test.go
bluesign Nov 25, 2021
6cc3263
Update runtime/tests/checker/interface_test.go
bluesign Nov 25, 2021
b75c3c5
Update runtime/tests/checker/interface_test.go
bluesign Nov 25, 2021
772916a
tabs to spaces in code literals
bluesign Nov 25, 2021
7504b0c
Update runtime/sema/check_composite_declaration.go
bluesign Nov 25, 2021
636f5a9
Update runtime/sema/check_composite_declaration.go
bluesign Nov 25, 2021
2151f1c
changes to comments
bluesign Nov 25, 2021
3099f37
apply default functions in separate loop, ranging over interface func…
turbolent Jan 17, 2022
b189f12
simplify
turbolent Jan 17, 2022
0b227fa
simplify and document function inheriting from interface
turbolent Jan 17, 2022
fa720f6
simplify
turbolent Jan 17, 2022
da610f9
simplify
turbolent Jan 17, 2022
82597c5
Apply suggestions from code review
turbolent Jan 17, 2022
3b41af3
Merge branch 'master' into master
turbolent Jan 17, 2022
01af2b3
move tests to separate file
turbolent Jan 17, 2022
a79f308
clean up tests
turbolent Jan 17, 2022
22aab34
add more tests
turbolent Jan 17, 2022
ad4f649
add tests for type requirements
turbolent Jan 18, 2022
25ddfe4
fix checking for multipel type requirements with default implementations
turbolent Jan 18, 2022
acdb83e
simplify
turbolent Jan 18, 2022
e5d400f
fix test, provide contract handler
turbolent Jan 18, 2022
7fade15
declare default functions of type requirement
turbolent Jan 18, 2022
d2c6bb8
fix checking of default functions in type requirements
turbolent Jan 18, 2022
968d06d
link in type requirement default functions
turbolent Jan 19, 2022
6e0c4db
lint
turbolent Jan 19, 2022
8e79098
Merge branch 'master' into bluesign/master
turbolent Jan 19, 2022
8b2d36e
Update runtime/interpreter/interpreter.go
bluesign Apr 26, 2022
2561bac
merge
bluesign Apr 27, 2022
a117b4f
Merge branch 'onflow-master'
bluesign Apr 27, 2022
1f21c2e
merge2
bluesign Apr 27, 2022
cd49c01
Error on implementation on special functions on interfaces
bluesign Apr 27, 2022
6c92d06
test for SpecialFunctionDefaultImplementationError
bluesign Apr 27, 2022
f2d4343
Merge branch 'master' of https://github.com/onflow/cadence into supun…
SupunS Jun 20, 2022
8327e9d
Sync with master
SupunS Jun 20, 2022
eb899b0
Add testcase for interface variable interpreting
SupunS Jun 20, 2022
03477ba
Merge branch 'master' into master
turbolent Aug 15, 2022
bcf231a
switch to new generic ordered map
turbolent Aug 15, 2022
3f9e74d
clean up
turbolent Aug 15, 2022
b1ad20e
check default special functions in type requirements
turbolent Aug 15, 2022
2d29b42
check assignment to effeciviely constant field in default function
turbolent Aug 15, 2022
94cea1c
simplify
turbolent Aug 16, 2022
dffc120
document interface default functions
turbolent Aug 16, 2022
5653108
Merge remote-tracking branch 'upstream/master'
turbolent Aug 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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])

}
}

Expand All @@ -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()])
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
}
}

Expand Down
67 changes: 45 additions & 22 deletions runtime/sema/check_composite_declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand All @@ -170,6 +172,7 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl
checkMissingMembers: checkMissingMembers,
interfaceTypeIsTypeRequirement: false,
},
&overridden,
)
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1017,23 +1022,39 @@ 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,
InterfaceMember: interfaceMember,
},
)
}

})

// Determine missing nested composite type definitions
Expand All @@ -1049,6 +1070,7 @@ func (checker *Checker) checkCompositeConformance(

nestedCompositeType, ok := compositeType.nestedTypes.Get(name)
if !ok {

missingNestedCompositeTypes = append(missingNestedCompositeTypes, requiredCompositeType)
return
}
Expand All @@ -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
}
Expand Down Expand Up @@ -1150,6 +1172,7 @@ func (checker *Checker) memberSatisfied(compositeMember, interfaceMember *Member

return false
}

}
}

Expand Down Expand Up @@ -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,
Expand All @@ -1272,6 +1296,7 @@ func (checker *Checker) checkTypeRequirement(
checkMissingMembers: true,
interfaceTypeIsTypeRequirement: true,
},
&overridden,
)
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1730,15 +1762,6 @@ func (checker *Checker) checkSpecialFunction(
)

switch containerKind {
case ContainerKindInterface:
Copy link
Member

Choose a reason for hiding this comment

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

Why was this removed?

Copy link
Member

Choose a reason for hiding this comment

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

I see that checkInterfaceSpecialFunctionBlock was inlined below (https://github.com/onflow/cadence/pull/1076/files#diff-c0b32cc7cd7dca791c9db58c1c67445a5d4f64f069f008ad62a2bbab14a0f48aR183), but is it OK to remove the call/check here?

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
Expand Down
59 changes: 22 additions & 37 deletions runtime/sema/check_interface_declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
}
}()
}
}
Expand Down Expand Up @@ -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,
},
)
}
}
20 changes: 20 additions & 0 deletions runtime/sema/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion runtime/sema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading