diff --git a/ROADMAP.md b/ROADMAP.md index b6060eadcf..5f13c6b312 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -79,11 +79,6 @@ listed in no particular order. Cadence should provide a way to define type aliases. - For example, if a contract interface might declare a type requirement `NFT`, - then all concrete conforming types must provide a concrete type `NFT`. - - However, it would be nice to give the type an additional, more useful name. - - `Word128` and `Word256` types Cadence should provide `Word128` and `Word256` types, just like it provides `UInt128` and `UInt256` diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index b5005a31ff..aedca1c56e 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -196,7 +196,7 @@ type CompositeTypeCode struct { type FunctionWrapper = func(inner FunctionValue) FunctionValue // WrapperCode contains the "prepared" / "callable" "code" -// for inherited types (interfaces and type requirements). +// for inherited types. // // These are "branch" nodes in the call chain, and are function wrappers, // i.e. they wrap the functions / function wrappers that inherit them. @@ -208,11 +208,10 @@ type WrapperCode struct { } // TypeCodes is the value which stores the "prepared" / "callable" "code" -// of all composite types, interface types, and type requirements. +// of all composite types and interface types. type TypeCodes struct { - CompositeCodes map[sema.TypeID]CompositeTypeCode - InterfaceCodes map[sema.TypeID]WrapperCode - TypeRequirementCodes map[sema.TypeID]WrapperCode + CompositeCodes map[sema.TypeID]CompositeTypeCode + InterfaceCodes map[sema.TypeID]WrapperCode } func (c TypeCodes) Merge(codes TypeCodes) { @@ -227,10 +226,6 @@ func (c TypeCodes) Merge(codes TypeCodes) { for typeID, code := range codes.InterfaceCodes { //nolint:maprange c.InterfaceCodes[typeID] = code } - - for typeID, code := range codes.TypeRequirementCodes { //nolint:maprange - c.TypeRequirementCodes[typeID] = code - } } type Storage interface { @@ -1191,26 +1186,12 @@ func (interpreter *Interpreter) declareNonEnumCompositeValue( } } - // NOTE: First the conditions of the type requirements are evaluated, - // then the conditions of this composite's conformances - // - // Because the conditions are wrappers, they have to be applied - // in reverse order: first the conformances, then the type requirements; - // each conformances and type requirements in reverse order as well. - conformances := compositeType.EffectiveInterfaceConformances() for i := len(conformances) - 1; i >= 0; i-- { conformance := conformances[i].InterfaceType wrapFunctions(interpreter.SharedState.typeCodes.InterfaceCodes[conformance.ID()]) } - typeRequirements := compositeType.TypeRequirements() - - for i := len(typeRequirements) - 1; i >= 0; i-- { - typeRequirement := typeRequirements[i] - wrapFunctions(interpreter.SharedState.typeCodes.TypeRequirementCodes[typeRequirement.ID()]) - } - interpreter.SharedState.typeCodes.CompositeCodes[compositeType.ID()] = CompositeTypeCode{ DestructorFunction: destructorFunction, CompositeFunctions: functions, @@ -2253,7 +2234,8 @@ func (interpreter *Interpreter) declareInterface( if nestedCompositeDeclaration.Kind() == common.CompositeKindEvent { interpreter.declareNonEnumCompositeValue(nestedCompositeDeclaration, lexicalScope) } else { - interpreter.declareTypeRequirement(nestedCompositeDeclaration, lexicalScope) + // this should be statically prevented in the checker + panic(errors.NewUnreachableError()) } } })() @@ -2278,46 +2260,6 @@ func (interpreter *Interpreter) declareInterface( } } -func (interpreter *Interpreter) declareTypeRequirement( - declaration *ast.CompositeDeclaration, - lexicalScope *VariableActivation, -) { - // Evaluate nested declarations in a new scope, so values - // of nested declarations won't be visible after the containing declaration - - (func() { - interpreter.activations.PushNewWithCurrent() - defer interpreter.activations.Pop() - - for _, nestedInterfaceDeclaration := range declaration.Members.Interfaces() { - interpreter.declareInterface(nestedInterfaceDeclaration, lexicalScope) - } - - for _, nestedCompositeDeclaration := range declaration.Members.Composites() { - interpreter.declareTypeRequirement(nestedCompositeDeclaration, lexicalScope) - } - })() - - compositeType := interpreter.Program.Elaboration.CompositeDeclarationType(declaration) - typeID := compositeType.ID() - - initializerFunctionWrapper := interpreter.initializerFunctionWrapper( - declaration.Members, - compositeType.ConstructorParameters, - lexicalScope, - ) - destructorFunctionWrapper := interpreter.destructorFunctionWrapper(declaration.Members, lexicalScope) - functionWrappers := interpreter.functionWrappers(declaration.Members, lexicalScope) - defaultFunctions := interpreter.defaultFunctions(declaration.Members, lexicalScope) - - interpreter.SharedState.typeCodes.TypeRequirementCodes[typeID] = WrapperCode{ - InitializerFunctionWrapper: initializerFunctionWrapper, - DestructorFunctionWrapper: destructorFunctionWrapper, - FunctionWrappers: functionWrappers, - Functions: defaultFunctions, - } -} - func (interpreter *Interpreter) initializerFunctionWrapper( members *ast.Members, parameters []sema.Parameter, diff --git a/runtime/interpreter/sharedstate.go b/runtime/interpreter/sharedstate.go index c7529d615b..9f2822a168 100644 --- a/runtime/interpreter/sharedstate.go +++ b/runtime/interpreter/sharedstate.go @@ -54,9 +54,8 @@ func NewSharedState(config *Config) *SharedState { allInterpreters: map[common.Location]*Interpreter{}, callStack: &CallStack{}, typeCodes: TypeCodes{ - CompositeCodes: map[sema.TypeID]CompositeTypeCode{}, - InterfaceCodes: map[sema.TypeID]WrapperCode{}, - TypeRequirementCodes: map[sema.TypeID]WrapperCode{}, + CompositeCodes: map[sema.TypeID]CompositeTypeCode{}, + InterfaceCodes: map[sema.TypeID]WrapperCode{}, }, inStorageIteration: false, storageMutatedDuringIteration: false, diff --git a/runtime/sema/check_composite_declaration.go b/runtime/sema/check_composite_declaration.go index 26d2b8fb75..64e0340017 100644 --- a/runtime/sema/check_composite_declaration.go +++ b/runtime/sema/check_composite_declaration.go @@ -26,7 +26,7 @@ import ( ) func (checker *Checker) VisitCompositeDeclaration(declaration *ast.CompositeDeclaration) (_ struct{}) { - checker.visitCompositeLikeDeclaration(declaration, ContainerKindComposite) + checker.visitCompositeLikeDeclaration(declaration) return } @@ -133,10 +133,10 @@ func (checker *Checker) checkAttachmentMembersAccess(attachmentType *CompositeTy } func (checker *Checker) VisitAttachmentDeclaration(declaration *ast.AttachmentDeclaration) (_ struct{}) { - return checker.visitAttachmentDeclaration(declaration, ContainerKindComposite) + return checker.visitAttachmentDeclaration(declaration) } -func (checker *Checker) visitAttachmentDeclaration(declaration *ast.AttachmentDeclaration, kind ContainerKind) (_ struct{}) { +func (checker *Checker) visitAttachmentDeclaration(declaration *ast.AttachmentDeclaration) (_ struct{}) { if !checker.Config.AttachmentsEnabled { checker.report(&AttachmentsNotEnabledError{ @@ -144,7 +144,7 @@ func (checker *Checker) visitAttachmentDeclaration(declaration *ast.AttachmentDe }) } - checker.visitCompositeLikeDeclaration(declaration, kind) + checker.visitCompositeLikeDeclaration(declaration) attachmentType := checker.Elaboration.CompositeDeclarationType(declaration) checker.checkAttachmentMembersAccess(attachmentType) checker.checkAttachmentBaseType( @@ -155,15 +155,12 @@ func (checker *Checker) visitAttachmentDeclaration(declaration *ast.AttachmentDe } // visitCompositeDeclaration checks a previously declared composite declaration. -// Checking behaviour depends on `kind`, i.e. if the composite declaration declares -// a composite (`kind` is `ContainerKindComposite`), or the composite declaration is -// nested in an interface and so acts as a type requirement (`kind` is `ContainerKindInterface`). // // NOTE: This function assumes that the composite type was previously declared using // `declareCompositeType` and exists in `checker.Elaboration.CompositeDeclarationTypes`, // and that the members and nested declarations for the composite type were declared // through `declareCompositeMembersAndValue`. -func (checker *Checker) visitCompositeLikeDeclaration(declaration ast.CompositeLikeDeclaration, kind ContainerKind) { +func (checker *Checker) visitCompositeLikeDeclaration(declaration ast.CompositeLikeDeclaration) { compositeType := checker.Elaboration.CompositeDeclarationType(declaration) if compositeType == nil { panic(errors.NewUnreachableError()) @@ -196,35 +193,30 @@ func (checker *Checker) visitCompositeLikeDeclaration(declaration ast.CompositeL checker.typeActivations.Enter() defer checker.typeActivations.Leave(declaration.EndPosition) - if kind == ContainerKindComposite { - checker.enterValueScope() - defer checker.leaveValueScope(declaration.EndPosition, false) - } + checker.enterValueScope() + defer checker.leaveValueScope(declaration.EndPosition, false) - checker.declareCompositeLikeNestedTypes(declaration, kind, true) + checker.declareCompositeLikeNestedTypes(declaration, ContainerKindComposite, true) var initializationInfo *InitializationInfo + // The initializer must initialize all members that are fields, + // e.g. not composite functions (which are by definition constant and "initialized") - if kind == ContainerKindComposite { - // The initializer must initialize all members that are fields, - // e.g. not composite functions (which are by definition constant and "initialized") + fields := members.Fields() + fieldMembers := orderedmap.New[MemberFieldDeclarationOrderedMap](len(fields)) - fields := members.Fields() - fieldMembers := orderedmap.New[MemberFieldDeclarationOrderedMap](len(fields)) - - for _, field := range fields { - fieldName := field.Identifier.Identifier - member, ok := compositeType.Members.Get(fieldName) - if !ok { - continue - } - - fieldMembers.Set(member, field) + for _, field := range fields { + fieldName := field.Identifier.Identifier + member, ok := compositeType.Members.Get(fieldName) + if !ok { + continue } - initializationInfo = NewInitializationInfo(compositeType, fieldMembers) + fieldMembers.Set(member, field) } + initializationInfo = NewInitializationInfo(compositeType, fieldMembers) + checker.checkInitializers( members.Initializers(), members.Fields(), @@ -232,36 +224,17 @@ func (checker *Checker) visitCompositeLikeDeclaration(declaration ast.CompositeL declaration.DeclarationDocString(), compositeType.ConstructorPurity, compositeType.ConstructorParameters, - kind, + ContainerKindComposite, initializationInfo, ) checker.checkUnknownSpecialFunctions(members.SpecialFunctions()) - switch kind { - case ContainerKindComposite: - checker.checkCompositeFunctions( - members.Functions(), - compositeType, - declaration.DeclarationDocString(), - ) - - case ContainerKindInterface: - checker.checkSpecialFunctionDefaultImplementation(declaration, "type requirement") - - declarationKind := declaration.Kind() - - checker.checkInterfaceFunctions( - members.Functions(), - compositeType, - declaration.DeclarationKind(), - &declarationKind, - declaration.DeclarationDocString(), - ) - - default: - panic(errors.NewUnreachableError()) - } + checker.checkCompositeFunctions( + members.Functions(), + compositeType, + declaration.DeclarationDocString(), + ) fieldPositionGetter := func(name string) ast.Position { return compositeType.FieldPosition(name, declaration) @@ -275,25 +248,8 @@ func (checker *Checker) visitCompositeLikeDeclaration(declaration ast.CompositeL ) // Check conformances - // NOTE: perform after completing composite type (e.g. setting constructor parameter types) - - // If the composite declaration is declaring a composite (`kind` is `ContainerKindComposite`), - // rather than a type requirement (`kind` is `ContainerKindInterface`), check that the composite - // conforms to all interfaces the composite declared it conforms to, i.e. all members match, - // and no members are missing. - - // If the composite declaration is a type requirement (`kind` is `ContainerKindInterface`), - // DON'T check that the composite conforms to all interfaces the composite declared it - // conforms to – these are requirements that the composite declaration of the implementation - // of the containing interface must conform to. - // - // Thus, missing members are valid, but still check that members that are declared as requirements - // match the members of the conformances (members in the interface) - - checkMissingMembers := kind != ContainerKindInterface inheritedMembers := map[string]struct{}{} - typeRequirementsInheritedMembers := map[string]map[string]struct{}{} for _, conformance := range compositeType.EffectiveInterfaceConformances() { checker.checkCompositeLikeConformance( @@ -302,11 +258,9 @@ func (checker *Checker) visitCompositeLikeDeclaration(declaration ast.CompositeL conformance.InterfaceType, conformance.ConformanceChainRoot, compositeConformanceCheckOptions{ - checkMissingMembers: checkMissingMembers, - interfaceTypeIsTypeRequirement: false, + checkMissingMembers: true, }, inheritedMembers, - typeRequirementsInheritedMembers, ) } @@ -320,7 +274,7 @@ func (checker *Checker) visitCompositeLikeDeclaration(declaration ast.CompositeL compositeType, declaration.DeclarationKind(), declaration.DeclarationDocString(), - kind, + ContainerKindComposite, ) }) @@ -451,7 +405,7 @@ func (checker *Checker) declareNestedDeclarations( ) { nestedDeclarations = map[string]ast.Declaration{} - // Only contracts and contract interfaces support nested composite declarations + // Only contracts support nested composite declarations if containerCompositeKind != common.CompositeKindContract { reportInvalidNesting := func(nestedDeclarationKind common.DeclarationKind, identifier ast.Identifier) { @@ -468,10 +422,15 @@ func (checker *Checker) declareNestedDeclarations( firstNestedCompositeDeclaration := nestedCompositeDeclarations[0] - reportInvalidNesting( - firstNestedCompositeDeclaration.DeclarationKind(), - firstNestedCompositeDeclaration.Identifier, - ) + // composite-in-interface nesting errors are already reported elsewhere + if (containerDeclarationKind != common.DeclarationKindStructureInterface && + containerDeclarationKind != common.DeclarationKindResourceInterface) || + firstNestedCompositeDeclaration.DeclarationKind() == common.DeclarationKindEvent { + reportInvalidNesting( + firstNestedCompositeDeclaration.DeclarationKind(), + firstNestedCompositeDeclaration.Identifier, + ) + } } else if len(nestedInterfaceDeclarations) > 0 { @@ -501,10 +460,14 @@ func (checker *Checker) declareNestedDeclarations( firstNestedAttachmentDeclaration := nestedAttachmentDeclaration[0] - reportInvalidNesting( - firstNestedAttachmentDeclaration.DeclarationKind(), - firstNestedAttachmentDeclaration.Identifier, - ) + // this error has already been reported elsewhere + if containerDeclarationKind != common.DeclarationKindStructureInterface && + containerDeclarationKind != common.DeclarationKindResourceInterface { + reportInvalidNesting( + firstNestedAttachmentDeclaration.DeclarationKind(), + firstNestedAttachmentDeclaration.Identifier, + ) + } } // NOTE: don't return, so nested declarations / types are still declared @@ -528,13 +491,17 @@ func (checker *Checker) declareNestedDeclarations( break default: - checker.report( - &InvalidNestedDeclarationError{ - NestedDeclarationKind: nestedDeclarationKind, - ContainerDeclarationKind: containerDeclarationKind, - Range: ast.NewRangeFromPositioned(checker.memoryGauge, identifier), - }, - ) + // if this is inside a contract interface, this error has already been reported elsewhere + if nestedDeclarationKind == common.DeclarationKindContractInterface || + containerDeclarationKind != common.DeclarationKindContractInterface { + checker.report( + &InvalidNestedDeclarationError{ + NestedDeclarationKind: nestedDeclarationKind, + ContainerDeclarationKind: containerDeclarationKind, + Range: ast.NewRangeFromPositioned(checker.memoryGauge, identifier), + }, + ) + } } } @@ -857,15 +824,6 @@ func (checker *Checker) declareCompositeLikeMembersAndValue( declareNestedComposite(nestedAttachmentDeclaration) } - // Declare implicit type requirement conformances, if any, - // after nested types are declared, and - // after explicit conformances are declared. - // - // For each nested composite type, check if a conformance - // declares a nested composite type with the same identifier, - // in which case it is a type requirement, - // and this nested composite type implicitly conforms to it. - compositeType.GetNestedTypes().Foreach(func(nestedTypeIdentifier string, nestedType Type) { nestedCompositeType, ok := nestedType.(*CompositeType) @@ -875,67 +833,6 @@ func (checker *Checker) declareCompositeLikeMembersAndValue( var inheritedMembers StringMemberOrderedMap - for _, compositeTypeConformance := range compositeType.EffectiveInterfaceConformances() { - conformanceNestedTypes := compositeTypeConformance.InterfaceType.GetNestedTypes() - - nestedType, ok := conformanceNestedTypes.Get(nestedTypeIdentifier) - if !ok { - continue - } - - typeRequirement, ok := nestedType.(*CompositeType) - if !ok { - continue - } - - 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.Get(memberName); ok { - errorRange := ast.NewRangeFromPositioned(checker.memoryGauge, declaration.DeclarationIdentifier()) - - if member.HasImplementation { - checker.report( - &MultipleInterfaceDefaultImplementationsError{ - CompositeKindedType: nestedCompositeType, - Member: member, - Range: errorRange, - }, - ) - } else { - checker.report( - &DefaultFunctionConflictError{ - CompositeKindedType: nestedCompositeType, - Member: member, - Range: errorRange, - }, - ) - } - - return - } - - if member.HasImplementation { - inheritedMembers.Set(memberName, member) - } - }) - - } - inheritedMembers.Foreach(func(memberName string, member *Member) { inheritedMember := *member inheritedMember.ContainerType = nestedCompositeType @@ -1333,8 +1230,7 @@ func (checker *Checker) enumRawType(declaration *ast.CompositeDeclaration) Type } type compositeConformanceCheckOptions struct { - checkMissingMembers bool - interfaceTypeIsTypeRequirement bool + checkMissingMembers bool } // checkCompositeLikeConformance checks if the given composite declaration with the given composite type @@ -1343,10 +1239,6 @@ type compositeConformanceCheckOptions struct { // 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) checkCompositeLikeConformance( compositeDeclaration ast.CompositeLikeDeclaration, compositeType *CompositeType, @@ -1354,8 +1246,6 @@ func (checker *Checker) checkCompositeLikeConformance( conformanceChainRoot *InterfaceType, options compositeConformanceCheckOptions, inheritedMembers map[string]struct{}, - // type requirement name -> inherited members - typeRequirementsInheritedMembers map[string]map[string]struct{}, ) { var missingMembers []*Member @@ -1462,33 +1352,6 @@ func (checker *Checker) checkCompositeLikeConformance( }) - // Determine missing nested composite type definitions - - conformance.NestedTypes.Foreach(func(name string, typeRequirement Type) { - - // Only non-event nested composite declarations are type requirements of the interface - - requiredCompositeType, ok := typeRequirement.(*CompositeType) - if !ok || requiredCompositeType.Kind == common.CompositeKindEvent { - return - } - - nestedCompositeType, ok := compositeType.NestedTypes.Get(name) - if !ok { - - missingNestedCompositeTypes = append(missingNestedCompositeTypes, requiredCompositeType) - return - } - - inherited := typeRequirementsInheritedMembers[name] - if inherited == nil { - inherited = map[string]struct{}{} - typeRequirementsInheritedMembers[name] = inherited - } - - checker.checkTypeRequirement(nestedCompositeType, compositeDeclaration, requiredCompositeType, inherited) - }) - if len(missingMembers) > 0 || len(memberMismatches) > 0 || len(missingNestedCompositeTypes) > 0 || @@ -1496,16 +1359,15 @@ func (checker *Checker) checkCompositeLikeConformance( checker.report( &ConformanceError{ - CompositeDeclaration: compositeDeclaration, - CompositeType: compositeType, - InterfaceType: conformanceChainRoot, - Pos: compositeDeclaration.DeclarationIdentifier().Pos, - InitializerMismatch: initializerMismatch, - MissingMembers: missingMembers, - MemberMismatches: memberMismatches, - MissingNestedCompositeTypes: missingNestedCompositeTypes, - InterfaceTypeIsTypeRequirement: options.interfaceTypeIsTypeRequirement, - NestedInterfaceType: conformance, + CompositeDeclaration: compositeDeclaration, + CompositeType: compositeType, + InterfaceType: conformanceChainRoot, + Pos: compositeDeclaration.DeclarationIdentifier().Pos, + InitializerMismatch: initializerMismatch, + MissingMembers: missingMembers, + MemberMismatches: memberMismatches, + MissingNestedCompositeTypes: missingNestedCompositeTypes, + NestedInterfaceType: conformance, }, ) } @@ -1538,8 +1400,7 @@ func (checker *Checker) checkConformanceKindMatch( conformances := conformingDeclaration.ConformanceList() if len(conformances) == 0 { - // For type requirements, there is no explicit conformance. - // Hence, log the error at the type requirement (i.e: declaration identifier) + // If there are no explicit conformances, log the error at the declaration identifier compositeKindMismatchIdentifier = conformingDeclaration.DeclarationIdentifier() } else { // Otherwise, find the conformance which resulted in the mismatch, @@ -1674,178 +1535,6 @@ func (checker *Checker) memberSatisfied( return !effectiveCompositeMemberAccess.IsLessPermissiveThan(effectiveInterfaceMemberAccess) } -// checkTypeRequirement checks conformance of a nested type declaration -// to a type requirement of an interface. -func (checker *Checker) checkTypeRequirement( - declaredType Type, - containerDeclaration ast.CompositeLikeDeclaration, - requiredCompositeType *CompositeType, - inherited map[string]struct{}, -) { - - members := containerDeclaration.DeclarationMembers() - - // A nested interface doesn't satisfy the type requirement, - // it must be a composite - - if declaredInterfaceType, ok := declaredType.(*InterfaceType); ok { - - // Find the interface declaration of the interface type - - var errorRange ast.Range - var foundInterfaceDeclaration bool - - for _, nestedInterfaceDeclaration := range members.Interfaces() { - nestedInterfaceIdentifier := nestedInterfaceDeclaration.Identifier.Identifier - if nestedInterfaceIdentifier == declaredInterfaceType.Identifier { - foundInterfaceDeclaration = true - errorRange = ast.NewRangeFromPositioned(checker.memoryGauge, nestedInterfaceDeclaration.Identifier) - break - } - } - - if !foundInterfaceDeclaration { - panic(errors.NewUnreachableError()) - } - - checker.report( - &DeclarationKindMismatchError{ - ExpectedDeclarationKind: requiredCompositeType.Kind.DeclarationKind(false), - ActualDeclarationKind: declaredInterfaceType.CompositeKind.DeclarationKind(true), - Range: errorRange, - }, - ) - - return - } - - // If the nested type is neither an interface nor a composite, - // something must be wrong in the checker - - declaredCompositeType, ok := declaredType.(*CompositeType) - if !ok { - panic(errors.NewUnreachableError()) - } - - // Find the composite declaration of the composite type - - var compositeDeclaration ast.CompositeLikeDeclaration - var foundRedeclaration bool - - findDeclaration := func(nestedCompositeDeclaration ast.CompositeLikeDeclaration) { - identifier := nestedCompositeDeclaration.DeclarationIdentifier() - nestedCompositeIdentifier := identifier.Identifier - if nestedCompositeIdentifier == declaredCompositeType.Identifier { - // If we detected a second nested composite declaration with the same identifier, - // report an error and stop further type requirement checking - if compositeDeclaration != nil { - foundRedeclaration = true - checker.report(&RedeclarationError{ - Kind: nestedCompositeDeclaration.DeclarationKind(), - Name: identifier.Identifier, - Pos: identifier.Pos, - PreviousPos: &compositeDeclaration.DeclarationIdentifier().Pos, - }) - } - compositeDeclaration = nestedCompositeDeclaration - // NOTE: Do not break / stop iteration, but keep looking for - // another (invalid) nested composite declaration with the same identifier, - // as the first found declaration is not necessarily the correct one - } - } - - for _, nestedCompositeDeclaration := range members.Composites() { - findDeclaration(nestedCompositeDeclaration) - } - - for _, nestedAttachmentDeclaration := range members.Attachments() { - findDeclaration(nestedAttachmentDeclaration) - } - - if foundRedeclaration { - return - } - - if compositeDeclaration == nil { - panic(errors.NewUnreachableError()) - } - - // Check that the composite declaration declares at least the conformances - // that the type requirement stated - - for _, requiredConformance := range requiredCompositeType.EffectiveInterfaceConformances() { - found := false - - for _, conformance := range declaredCompositeType.EffectiveInterfaceConformances() { - if conformance.InterfaceType == requiredConformance.InterfaceType { - found = true - break - } - - } - - if !found { - checker.report( - &MissingConformanceError{ - CompositeType: declaredCompositeType, - InterfaceType: requiredConformance.InterfaceType, - Range: ast.NewRangeFromPositioned(checker.memoryGauge, compositeDeclaration.DeclarationIdentifier()), - }, - ) - } - - } - - // Check the conformance of the composite to the type requirement - // like a top-level composite declaration to an interface type - - requiredInterfaceType := requiredCompositeType.InterfaceType() - - // while attachments cannot be declared as interfaces, an attachment type requirement essentially functions - // as an interface, so we must enforce that the concrete attachment's base type is a compatible with the requirement's. - // Specifically, attachment base types are contravariant; if the contract interface requires a struct attachment with a base type - // of `S`, the concrete contract can fulfill this requirement by implementing an attachment with a base type of `AnyStruct`: - // if the attachment is valid on any structure, then clearly it is a valid attachment for `S`. See the example below: - // - // resource interface RI { /* ... */ } - // resource R: RI { /* ... */ } - // contract interface CI { - // attachment A for R { /* ... */ } - // } - // contract C: CI { - // attachment A for RI { /* ... */ } - // } - // - // In this example, as long as `A` in `C` contains the expected member declarations as defined in `CI`, this is a valid - // implementation of the type requirement, as an `A` that can accept any `RI` as a base can clearly function for an `R` as well. - // It may also be helpful to conceptualize an attachment as a sort of implicit function that takes a `base` argument and returns a composite value. - if requiredCompositeType.Kind == common.CompositeKindAttachment && declaredCompositeType.Kind == common.CompositeKindAttachment { - if !IsSubType(requiredCompositeType.baseType, declaredCompositeType.baseType) { - checker.report( - &ConformanceError{ - CompositeDeclaration: compositeDeclaration, - CompositeType: declaredCompositeType, - InterfaceType: requiredCompositeType.InterfaceType(), - Pos: compositeDeclaration.DeclarationIdentifier().Pos, - }, - ) - } - } - - checker.checkCompositeLikeConformance( - compositeDeclaration, - declaredCompositeType, - requiredInterfaceType, - requiredInterfaceType, - compositeConformanceCheckOptions{ - checkMissingMembers: true, - interfaceTypeIsTypeRequirement: true, - }, - inherited, - map[string]map[string]struct{}{}, - ) -} - func CompositeLikeConstructorType( elaboration *Elaboration, compositeDeclaration ast.CompositeLikeDeclaration, diff --git a/runtime/sema/check_interface_declaration.go b/runtime/sema/check_interface_declaration.go index 1436b9c6fd..17bed65fe1 100644 --- a/runtime/sema/check_interface_declaration.go +++ b/runtime/sema/check_interface_declaration.go @@ -156,21 +156,26 @@ func (checker *Checker) VisitInterfaceDeclaration(declaration *ast.InterfaceDecl } for _, nestedComposite := range declaration.Members.Composites() { - // Non-event composite declarations nested in interface declarations are type requirements, - // i.e. they should be checked like interfaces - + // only event types may be declared in interfaces if nestedComposite.Kind() != common.CompositeKindEvent { - checker.visitCompositeLikeDeclaration(nestedComposite, kind) + checker.report(&InvalidNestedDeclarationError{ + NestedDeclarationKind: nestedComposite.DeclarationKind(), + ContainerDeclarationKind: declaration.DeclarationKind(), + Range: declaration.Range, + }) } else { - // events should be checked like composites, as they are not type requirements - checker.visitCompositeLikeDeclaration(nestedComposite, ContainerKindComposite) + // events should be checked like composites + checker.visitCompositeLikeDeclaration(nestedComposite) } } - for _, nestedAttachments := range declaration.Members.Attachments() { - // Attachment declarations nested in interface declarations are type requirements, - // i.e. they should be checked like interfaces - checker.visitAttachmentDeclaration(nestedAttachments, kind) + for _, nestedAttachment := range declaration.Members.Attachments() { + // only event types may be declared in interfaces + checker.report(&InvalidNestedDeclarationError{ + NestedDeclarationKind: nestedAttachment.DeclarationKind(), + ContainerDeclarationKind: declaration.DeclarationKind(), + Range: declaration.Range, + }) } return @@ -446,14 +451,8 @@ func (checker *Checker) declareInterfaceMembersAndValue(declaration *ast.Interfa for _, nestedCompositeDeclaration := range declaration.Members.Composites() { if nestedCompositeDeclaration.Kind() == common.CompositeKindEvent { declareNestedEvent(nestedCompositeDeclaration) - } else { - checker.declareCompositeLikeMembersAndValue(nestedCompositeDeclaration, ContainerKindInterface) } } - - for _, nestedAttachmentDeclaration := range declaration.Members.Attachments() { - checker.declareAttachmentMembersAndValue(nestedAttachmentDeclaration, ContainerKindInterface) - } })() } @@ -638,55 +637,6 @@ func (checker *Checker) checkInterfaceConformance( inheritedMembers[name] = conformanceMember } }) - - // Check for nested type conflicts - - reportTypeConflictError := func(typeName string, typ CompositeKindedType, otherType Type) { - otherCompositeType, ok := otherType.(CompositeKindedType) - if !ok { - return - } - - _, isInterface := typ.(*InterfaceType) - _, isOtherTypeInterface := otherCompositeType.(*InterfaceType) - - checker.report(&InterfaceMemberConflictError{ - InterfaceType: interfaceType, - ConflictingInterfaceType: conformance, - MemberName: typeName, - MemberKind: typ.GetCompositeKind().DeclarationKind(isInterface), - ConflictingMemberKind: otherCompositeType.GetCompositeKind().DeclarationKind(isOtherTypeInterface), - Range: ast.NewRangeFromPositioned( - checker.memoryGauge, - interfaceDeclaration.Identifier, - ), - }) - } - - conformance.NestedTypes.Foreach(func(name string, typeRequirement Type) { - compositeType, ok := typeRequirement.(CompositeKindedType) - if !ok { - return - } - - // Check if the type definitions coming from other conformances have conflicts. - if inheritedType, ok := inheritedNestedTypes[name]; ok { - inheritedCompositeType, ok := inheritedType.(CompositeKindedType) - if !ok { - return - } - - reportTypeConflictError(name, compositeType, inheritedCompositeType) - } - - inheritedNestedTypes[name] = typeRequirement - - // Check if the type definitions coming from the current declaration have conflicts. - nestedType, ok := interfaceType.NestedTypes.Get(name) - if ok { - reportTypeConflictError(name, compositeType, nestedType) - } - }) } func (checker *Checker) checkDuplicateInterfaceMember( diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 0efa538b92..4de9858773 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -1441,16 +1441,15 @@ type InitializerMismatch struct { InterfaceParameters []Parameter } type ConformanceError struct { - CompositeDeclaration ast.CompositeLikeDeclaration - CompositeType *CompositeType - InterfaceType *InterfaceType - NestedInterfaceType *InterfaceType - InitializerMismatch *InitializerMismatch - MissingMembers []*Member - MemberMismatches []MemberMismatch - MissingNestedCompositeTypes []*CompositeType - Pos ast.Position - InterfaceTypeIsTypeRequirement bool + CompositeDeclaration ast.CompositeLikeDeclaration + CompositeType *CompositeType + InterfaceType *InterfaceType + NestedInterfaceType *InterfaceType + InitializerMismatch *InitializerMismatch + MissingMembers []*Member + MemberMismatches []MemberMismatch + MissingNestedCompositeTypes []*CompositeType + Pos ast.Position } var _ SemanticError = &ConformanceError{} @@ -1462,19 +1461,11 @@ func (*ConformanceError) isSemanticError() {} func (*ConformanceError) IsUserError() {} func (e *ConformanceError) Error() string { - var interfaceDescription string - if e.InterfaceTypeIsTypeRequirement { - interfaceDescription = "type requirement" - } else { - interfaceDescription = "interface" - } - return fmt.Sprintf( - "%s `%s` does not conform to %s %s `%s`", + "%s `%s` does not conform to %s interface `%s`", e.CompositeType.Kind.Name(), e.CompositeType.QualifiedString(), e.InterfaceType.CompositeKind.Name(), - interfaceDescription, e.InterfaceType.QualifiedString(), ) } @@ -1540,14 +1531,6 @@ func (e *ConformanceError) ErrorNotes() (notes []errors.ErrorNote) { }) } - if e.NestedInterfaceType != e.InterfaceType { - compositeIdentifierRange := ast.NewUnmeteredRangeFromPositioned(e.CompositeDeclaration.DeclarationIdentifier()) - notes = append(notes, &NestedConformanceMismatchNote{ - nestedInterfaceType: e.NestedInterfaceType, - Range: compositeIdentifierRange, - }) - } - return } @@ -1561,20 +1544,6 @@ func (n MemberMismatchNote) Message() string { return "mismatch here" } -// NestedConformanceMismatchNote - -type NestedConformanceMismatchNote struct { - nestedInterfaceType *InterfaceType - ast.Range -} - -func (n NestedConformanceMismatchNote) Message() string { - return fmt.Sprintf( - "does not conform to nested interface requirement `%s`", - n.nestedInterfaceType, - ) -} - // DuplicateConformanceError // // TODO: just make this a warning? diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 0f35a03296..8b10275cae 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3993,12 +3993,11 @@ type CompositeType struct { TypeID TypeID QualifiedIdentifier string } - Members *StringMemberOrderedMap - memberResolvers map[string]MemberResolver - Identifier string - Fields []string - ConstructorParameters []Parameter - ImplicitTypeRequirementConformances []*CompositeType + Members *StringMemberOrderedMap + memberResolvers map[string]MemberResolver + Identifier string + Fields []string + ConstructorParameters []Parameter // an internal set of field `effectiveInterfaceConformances` effectiveInterfaceConformanceSet *InterfaceSet effectiveInterfaceConformances []Conformance @@ -4053,11 +4052,6 @@ func (t *CompositeType) EffectiveInterfaceConformances() []Conformance { return t.effectiveInterfaceConformances } -func (t *CompositeType) addImplicitTypeRequirementConformance(typeRequirement *CompositeType) { - t.ImplicitTypeRequirementConformances = - append(t.ImplicitTypeRequirementConformances, typeRequirement) -} - func (*CompositeType) IsType() {} func (t *CompositeType) String() string { @@ -4353,29 +4347,6 @@ func (t *CompositeType) InterfaceType() *InterfaceType { } } -func (t *CompositeType) TypeRequirements() []*CompositeType { - - var typeRequirements []*CompositeType - - if containerComposite, ok := t.containerType.(*CompositeType); ok { - for _, conformance := range containerComposite.EffectiveInterfaceConformances() { - ty, ok := conformance.InterfaceType.NestedTypes.Get(t.Identifier) - if !ok { - continue - } - - typeRequirement, ok := ty.(*CompositeType) - if !ok { - continue - } - - typeRequirements = append(typeRequirements, typeRequirement) - } - } - - return typeRequirements -} - func (*CompositeType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { // TODO: return false @@ -6260,21 +6231,15 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { // NOTE: type equality case (composite type `T` is subtype of composite type `U`) // is already handled at beginning of function - switch typedSubType := subType.(type) { + switch subType.(type) { case *IntersectionType: // A intersection type `{Us}` is never a subtype of a type `V`: return false case *CompositeType: - // The supertype composite type might be a type requirement. - // Check if the subtype composite type implicitly conforms to it. - - for _, conformance := range typedSubType.ImplicitTypeRequirementConformances { - if conformance == typedSuperType { - return true - } - } + // Non-equal composite types are never subtypes of each other + return false } case *InterfaceType: diff --git a/runtime/tests/checker/access_test.go b/runtime/tests/checker/access_test.go index 1891eb6909..2851d52214 100644 --- a/runtime/tests/checker/access_test.go +++ b/runtime/tests/checker/access_test.go @@ -2134,38 +2134,6 @@ func TestCheckRestrictiveAccessModifier(t *testing.T) { t.Run(access.Keyword(), func(t *testing.T) { - t.Run("type requirement", func(t *testing.T) { - - _, err := ParseAndCheck(t, - fmt.Sprintf( - ` - access(all) contract interface CI { - - access(all) resource R { - - %[1]s var x: Int - } - } - - access(all) contract C: CI { - - access(all) resource R { - - %[1]s var x: Int - - init () { - self.x = 0 - } - } - } - `, - access.Keyword(), - ), - ) - - require.NoError(t, err) - }) - t.Run("interface", func(t *testing.T) { _, err := ParseAndCheck(t, @@ -2210,26 +2178,6 @@ func TestCheckInvalidRestrictiveAccessModifier(t *testing.T) { t.Run(access.Keyword(), func(t *testing.T) { - t.Run("type requirement", func(t *testing.T) { - - _, err := ParseAndCheck(t, - fmt.Sprintf( - ` - access(all) contract interface CI { - - access(all) resource R { - - %[1]s var x: Int - } - } - `, - access.Keyword(), - ), - ) - - expectInvalidAccessModifierError(t, err) - }) - t.Run("interface", func(t *testing.T) { _, err := ParseAndCheck(t, diff --git a/runtime/tests/checker/attachments_test.go b/runtime/tests/checker/attachments_test.go index c50a66eb5a..fa41a5f19d 100644 --- a/runtime/tests/checker/attachments_test.go +++ b/runtime/tests/checker/attachments_test.go @@ -420,23 +420,6 @@ func TestCheckNestedBaseType(t *testing.T) { assert.IsType(t, &sema.InvalidBaseTypeError{}, errs[0]) }) - t.Run("contract interface", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - attachment A for Test {} - } - `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.InvalidBaseTypeError{}, errs[0]) - }) - t.Run("qualified base type", func(t *testing.T) { t.Parallel() @@ -481,7 +464,7 @@ func TestCheckNestedBaseType(t *testing.T) { }) } -func TestCheckTypeRequirement(t *testing.T) { +func TestCheckTypeRequirementsNoLongerAllowed(t *testing.T) { t.Parallel() @@ -496,195 +479,12 @@ func TestCheckTypeRequirement(t *testing.T) { fun foo(): Int } } - contract C: Test { - - } - `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.ConformanceError{}, errs[0]) - }) - - t.Run("concrete struct", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - attachment A for AnyStruct {} - } - contract C: Test { - struct A {} - } - `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[0]) - }) - - t.Run("concrete resource", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - attachment A for AnyStruct {} - } - contract C: Test { - resource A {} - } - `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[0]) - }) - - t.Run("missing method", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - attachment A for AnyStruct { - fun foo(): Int - } - } - contract C: Test { - attachment A for AnyStruct { - - } - } - `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.ConformanceError{}, errs[0]) - }) - - t.Run("missing field", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - attachment A for AnyStruct { - let x: Int - } - } - contract C: Test { - attachment A for AnyStruct { - - } - } - `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.ConformanceError{}, errs[0]) - }) - - t.Run("incompatible base type", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - struct S {} - attachment A for S { - } - } - contract C: Test { - struct S {} - struct S2 {} - attachment A for S2 { - } - } `, ) errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.ConformanceError{}, errs[0]) - }) - - t.Run("basetype subtype", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - attachment A for AnyStruct { - } - } - contract C: Test { - struct S {} - attachment A for S { - } - } - `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.ConformanceError{}, errs[0]) - }) - - t.Run("base type Basetype", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - struct S {} - attachment A for S { - } - } - contract C: Test { - struct S {} - attachment A for AnyStruct { - } - } - `, - ) - - require.NoError(t, err) - }) - - t.Run("conforms", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - attachment A for AnyStruct { - fun foo(): Int - } - } - contract C: Test { - attachment A for AnyStruct { - fun foo(): Int {return 3} - } - } - `, - ) - - require.NoError(t, err) + assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) }) } diff --git a/runtime/tests/checker/conformance_test.go b/runtime/tests/checker/conformance_test.go index ea92bc1e9b..c50896d743 100644 --- a/runtime/tests/checker/conformance_test.go +++ b/runtime/tests/checker/conformance_test.go @@ -19,7 +19,6 @@ package checker import ( - "fmt" "testing" "github.com/stretchr/testify/require" @@ -31,9 +30,6 @@ import ( func TestCheckEventNonTypeRequirementConformance(t *testing.T) { t.Parallel() - - // events do not create type requirements - _, err := ParseAndCheck(t, ` access(all) contract interface CI { @@ -49,224 +45,6 @@ func TestCheckEventNonTypeRequirementConformance(t *testing.T) { require.NoError(t, err) } -func TestCheckTypeRequirementConformance(t *testing.T) { - - t.Parallel() - - test := func(preparationCode string, interfaceCode string, conformanceCode string, valid bool) { - _, err := ParseAndCheck(t, - fmt.Sprintf( - ` - %s - - access(all) contract interface CI { - %s - } - - access(all) contract C: CI { - %s - } - `, - preparationCode, - interfaceCode, - conformanceCode, - ), - ) - - if valid { - require.NoError(t, err) - } else { - errs := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.ConformanceError{}, errs[0]) - } - } - - t.Run("Both empty", func(t *testing.T) { - - t.Parallel() - - test( - ``, - `access(all) struct S {}`, - `access(all) struct S {}`, - true, - ) - }) - - t.Run("Conformance with additional function", func(t *testing.T) { - - t.Parallel() - - test( - ``, - ` - access(all) struct S {} - `, - ` - access(all) struct S { - fun foo() {} - } - `, - true, - ) - }) - - t.Run("Conformance with missing function", func(t *testing.T) { - - t.Parallel() - - test( - ``, - ` - access(all) struct S { - fun foo() - } - `, - ` - access(all) struct S {} - `, - false, - ) - }) - - t.Run("Conformance with same name, same parameter type, but different argument label", func(t *testing.T) { - - t.Parallel() - - test( - ``, - ` - access(all) struct S { - fun foo(x: Int) - } - `, - ` - access(all) struct S { - fun foo(y: Int) {} - } - `, - false, - ) - }) - - t.Run("Conformance with same name, same argument label, but different parameter type", func(t *testing.T) { - - t.Parallel() - - test( - ``, - ` - access(all) struct S { - fun foo(x: Int) - } - `, - ` - access(all) struct S { - fun foo(x: String) {} - } - `, - false, - ) - }) - - t.Run("Conformance with same name, same argument label, same parameter type, different parameter name", func(t *testing.T) { - - t.Parallel() - - test( - ``, - ` - access(all) struct S { - fun foo(x y: String) - } - `, - ` - access(all) struct S { - fun foo(x z: String) {} - } - `, - true, - ) - }) - - t.Run("Conformance with more specific parameter type", func(t *testing.T) { - - t.Parallel() - - test( - ` - access(all) struct interface I {} - access(all) struct T: I {} - `, - ` - access(all) struct S { - fun foo(bar: {I}) - } - `, - ` - access(all) struct S { - fun foo(bar: T) {} - } - `, - false, - ) - }) - - t.Run("Conformance with same nested parameter type", func(t *testing.T) { - - t.Parallel() - - test( - ` - access(all) contract X { - struct Bar {} - } - `, - ` - access(all) struct S { - fun foo(bar: X.Bar) - } - `, - ` - access(all) struct S { - fun foo(bar: X.Bar) {} - } - `, - true, - ) - }) - - t.Run("Conformance with different nested parameter type", func(t *testing.T) { - - t.Parallel() - - test( - ` - access(all) contract X { - struct Bar {} - } - - access(all) contract Y { - struct Bar {} - } - `, - ` - access(all) struct S { - fun foo(bar: X.Bar) - } - `, - ` - access(all) struct S { - fun foo(bar: Y.Bar) {} - } - `, - false, - ) - - }) -} - func TestCheckConformanceWithFunctionSubtype(t *testing.T) { t.Parallel() @@ -416,89 +194,6 @@ func TestCheckConformanceWithFunctionSubtype(t *testing.T) { }) } -func TestCheckTypeRequirementDuplicateDeclaration(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface CI { - // Checking if CI_TR1 conforms to CI here, - // requires checking the type requirement CI_TR2 of CI. - // - // Note that CI_TR1 here declares 2 (!) - // nested composite declarations named CI_TR2. - // - // Checking should not just use the first declaration named CI_TR2, - // but detect the second / duplicate, error, - // and stop further conformance checking - // - contract CI_TR1: CI { - contract CI_TR2 {} - contract CI_TR2: CI { - contract CI_TR2_TR {} - } - } - - contract CI_TR2: CI { - contract CI_TR2_TR {} - } - } - `) - - errs := RequireCheckerErrors(t, err, 13) - - require.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) - require.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[1]) - require.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[2]) - require.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[3]) - require.IsType(t, &sema.RedeclarationError{}, errs[4]) - require.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[5]) - require.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[6]) - require.IsType(t, &sema.RedeclarationError{}, errs[7]) - require.IsType(t, &sema.RedeclarationError{}, errs[8]) - require.IsType(t, &sema.RedeclarationError{}, errs[9]) - require.IsType(t, &sema.ConformanceError{}, errs[10]) - 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 := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.ConformanceError{}, errs[0]) -} - func TestCheckInitializerConformanceErrorMessages(t *testing.T) { t.Parallel() @@ -578,66 +273,4 @@ func TestCheckInitializerConformanceErrorMessages(t *testing.T) { conformanceErr := errs[0].(*sema.ConformanceError) require.Equal(t, "`R` is missing definitions for members: `foo`, `bar`", conformanceErr.SecondaryError()) }) - - t.Run("1 missing type", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - access(all) contract interface I { - access(all) struct S {} - } - - access(all) contract C: I { - } - `) - - errs := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.ConformanceError{}, errs[0]) - conformanceErr := errs[0].(*sema.ConformanceError) - require.Equal(t, "`C` is missing definitions for types: `I.S`", conformanceErr.SecondaryError()) - }) - - t.Run("2 missing type", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - access(all) contract interface I { - access(all) struct S {} - access(all) resource R {} - } - - access(all) contract C: I { - } - `) - - errs := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.ConformanceError{}, errs[0]) - conformanceErr := errs[0].(*sema.ConformanceError) - require.Equal(t, "`C` is missing definitions for types: `I.S`, `I.R`", conformanceErr.SecondaryError()) - }) - - t.Run("missing type and member", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - access(all) contract interface I { - access(all) struct S {} - access(all) fun foo() - } - - access(all) contract C: I { - } - `) - - errs := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.ConformanceError{}, errs[0]) - conformanceErr := errs[0].(*sema.ConformanceError) - require.Equal(t, "`C` is missing definitions for members: `foo`. `C` is also missing definitions for types: `I.S`", conformanceErr.SecondaryError()) - }) } diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index 85621c47fa..807c54f882 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -1525,296 +1525,109 @@ func TestCheckInterfaceSelfUse(t *testing.T) { } } -func TestCheckInvalidContractInterfaceConformanceMissingTypeRequirement(t *testing.T) { +func TestCheckInvalidTypeRequirementDeclaration(t *testing.T) { t.Parallel() - _, err := ParseAndCheck(t, - ` + t.Run("struct", func(t *testing.T) { + _, err := ParseAndCheck(t, + ` contract interface Test { struct Nested {} } - - contract TestImpl: Test { - // missing 'Nested' - } `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.ConformanceError{}, errs[0]) -} + ) -func TestCheckInvalidContractInterfaceConformanceTypeRequirementKindMismatch(t *testing.T) { + errs := RequireCheckerErrors(t, err, 1) - t.Parallel() + assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) + }) - _, err := ParseAndCheck(t, - ` + t.Run("struct interface ok", func(t *testing.T) { + _, err := ParseAndCheck(t, + ` contract interface Test { - struct Nested {} - } - - contract TestImpl: Test { - // expected struct, not struct interface struct interface Nested {} } + contract C { + struct S: Test.Nested {} + } `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.DeclarationKindMismatchError{}, errs[0]) -} - -func TestCheckInvalidContractInterfaceConformanceTypeRequirementMismatch(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - struct Nested {} - } - - contract TestImpl: Test { - // expected struct - resource Nested {} - } - `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[0]) -} - -func TestCheckContractInterfaceTypeRequirement(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - struct Nested { - fun test(): Int - } - } - `, - ) - - require.NoError(t, err) -} - -func TestCheckContractInterfaceTypeRequirementFunctionImplementation(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - struct Nested { - fun test(): Int { - return 1 - } - } - } - `, - ) - - require.NoError(t, err) - -} - -func TestCheckInvalidContractInterfaceTypeRequirementMissingFunction(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - contract interface Test { - struct Nested { - fun test(): Int - } - } - - contract TestImpl: Test { - struct Nested { - // missing function 'test' - } - } - `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.ConformanceError{}, errs[0]) -} - -func TestCheckContractInterfaceTypeRequirementWithFunction(t *testing.T) { + ) - t.Parallel() + require.NoError(t, err) + }) - _, err := ParseAndCheck(t, - ` + t.Run("resource", func(t *testing.T) { + _, err := ParseAndCheck(t, + ` contract interface Test { - struct Nested { - fun test(): Int - } - } - - contract TestImpl: Test { - struct Nested { - fun test(): Int { - return 1 - } - } + resource Nested {} } `, - ) - - require.NoError(t, err) -} + ) -func TestCheckContractInterfaceTypeRequirementConformanceMissingMembers(t *testing.T) { + errs := RequireCheckerErrors(t, err, 1) - t.Parallel() + assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) + }) - _, err := ParseAndCheck(t, - ` + t.Run("resource interface ok", func(t *testing.T) { + _, err := ParseAndCheck(t, + ` contract interface Test { - - struct interface NestedInterface { - fun test(): Bool - } - - struct Nested: NestedInterface { - // missing function 'test' is valid: - // 'Nested' is a requirement, not an actual declaration - } + resource interface Nested {} } + contract C { + resource S: Test.Nested {} + } `, - ) - - require.NoError(t, err) -} - -func TestCheckInvalidContractInterfaceTypeRequirementConformance(t *testing.T) { + ) - t.Parallel() + require.NoError(t, err) + }) - _, err := ParseAndCheck(t, - ` + t.Run("enum", func(t *testing.T) { + _, err := ParseAndCheck(t, + ` contract interface Test { - - struct interface NestedInterface { - fun test(): Bool - } - - struct Nested: NestedInterface { - // return type mismatch, should be 'Bool' - fun test(): Int - } + enum Nested: Int {} } `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.ConformanceError{}, errs[0]) -} + ) -func TestCheckInvalidContractInterfaceTypeRequirementConformanceMissingFunction(t *testing.T) { + errs := RequireCheckerErrors(t, err, 1) - t.Parallel() + assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) + }) - _, err := ParseAndCheck(t, - ` + t.Run("contract", func(t *testing.T) { + _, err := ParseAndCheck(t, + ` contract interface Test { - - struct interface NestedInterface { - fun test(): Bool - } - - struct Nested: NestedInterface {} - } - - contract TestImpl: Test { - - struct Nested: Test.NestedInterface { - // missing function 'test' - } + contract Nested {} } `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.ConformanceError{}, errs[0]) -} + ) -func TestCheckInvalidContractInterfaceTypeRequirementMissingConformance(t *testing.T) { + errs := RequireCheckerErrors(t, err, 1) - t.Parallel() + assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) + }) - _, err := ParseAndCheck(t, - ` + t.Run("contract interface", func(t *testing.T) { + _, err := ParseAndCheck(t, + ` contract interface Test { - - struct interface NestedInterface { - fun test(): Bool - } - - struct Nested: NestedInterface {} - } - - contract TestImpl: Test { - - // missing conformance to 'Test.NestedInterface' - struct Nested { - fun test(): Bool { - return true - } - } + contract interface Nested {} } `, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.MissingConformanceError{}, errs[0]) -} - -func TestCheckContractInterfaceTypeRequirementImplementation(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, - ` - struct interface OtherInterface {} - - contract interface Test { - - struct interface NestedInterface { - fun test(): Bool - } - - struct Nested: NestedInterface {} - } - - contract TestImpl: Test { + ) - struct Nested: Test.NestedInterface, OtherInterface { - fun test(): Bool { - return true - } - } - } - `, - ) + errs := RequireCheckerErrors(t, err, 1) - require.NoError(t, err) + assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) + }) } func TestCheckContractInterfaceFungibleToken(t *testing.T) { @@ -2012,44 +1825,6 @@ func TestCheckInvalidMultipleInterfaceDefaultImplementation(t *testing.T) { require.IsType(t, &sema.MultipleInterfaceDefaultImplementationsError{}, errs[0]) }) - - 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 Test.X().test() - } - `) - - errs := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.MultipleInterfaceDefaultImplementationsError{}, errs[0]) - }) } func TestCheckMultipleInterfaceDefaultImplementationWhenOverriden(t *testing.T) { @@ -2086,49 +1861,43 @@ func TestCheckMultipleInterfaceDefaultImplementationWhenOverriden(t *testing.T) require.NoError(t, err) }) +} + +func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T) { + + t.Parallel() - t.Run("type requirement", func(t *testing.T) { + t.Run("interface", func(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` - contract interface IA { - - struct X { - fun test(): Int { - return 41 - } + struct interface IA { + fun test(): Int { + return 41 } } - contract interface IB { - - struct X { - fun test(): Int { - return 41 - } - } + struct interface IB { + fun test(): Int } - contract Test: IA, IB { + struct Test: IA, IB { - struct X { - fun test(): Int { - return 42 - } - } } fun test(): Int { - return Test.X().test() + return Test().test() } `) - require.NoError(t, err) + errs := RequireCheckerErrors(t, err, 1) + + require.IsType(t, &sema.DefaultFunctionConflictError{}, errs[0]) }) } -func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T) { +func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridden(t *testing.T) { t.Parallel() @@ -2148,55 +1917,45 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementation(t *testing.T } struct Test: IA, IB { - + fun test(): Int { + return 42 + } } fun test(): Int { return Test().test() } `) + require.NoError(t, err) + }) +} - errs := RequireCheckerErrors(t, err, 1) +func TestCheckInterfaceDefaultImplementation(t *testing.T) { - require.IsType(t, &sema.DefaultFunctionConflictError{}, errs[0]) - }) + t.Parallel() - t.Run("type requirement", func(t *testing.T) { + t.Run("interface", 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 + struct interface IA { + fun test(): Int { + return 42 } } - contract Test: IA, IB { - struct X {} - } + struct Test: IA {} fun test(): Int { - return Test.X().test() + return Test().test() } `) - - errs := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.DefaultFunctionConflictError{}, errs[0]) + require.NoError(t, err) }) } -func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridden(t *testing.T) { +func TestCheckInterfaceDefaultImplementationOverriden(t *testing.T) { t.Parallel() @@ -2211,11 +1970,7 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridde } } - struct interface IB { - fun test(): Int - } - - struct Test: IA, IB { + struct Test: IA { fun test(): Int { return 42 } @@ -2227,177 +1982,30 @@ func TestCheckMultipleInterfaceSingleInterfaceDefaultImplementationWhenOverridde `) require.NoError(t, err) }) +} + +func TestSpecialFunctionDefaultImplementationUsage(t *testing.T) { + + t.Parallel() - t.Run("type requirement", func(t *testing.T) { + t.Run("interface", func(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` - contract interface IA { + struct interface IA { + var x: Int - struct X { - fun test(): Int { - return 41 - } + init() { + self.x = 1 } } - contract interface IB { + struct Test: IA { + var x: Int - struct X { - fun test(): Int - } - } - - contract Test: IA, IB { - - struct X { - fun test(): Int { - return 42 - } - } - } - - fun test(): Int { - return Test.X().test() - } - `) - 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 TestSpecialFunctionDefaultImplementationUsage(t *testing.T) { - - t.Parallel() - - t.Run("interface", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - struct interface IA { - var x: Int - - init() { - self.x = 1 - } - } - - struct Test: IA { - var x: Int - - init() { - self.x = 0 + init() { + self.x = 0 } } `) @@ -2406,43 +2014,6 @@ 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 := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.SpecialFunctionDefaultImplementationError{}, errs[0]) - }) } func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { @@ -2477,41 +2048,6 @@ 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 := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0]) - }) } func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage2(t *testing.T) { @@ -2548,43 +2084,6 @@ func TestCheckInvalidInterfaceDefaultImplementationConcreteTypeUsage2(t *testing 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 := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.AssignmentToConstantMemberError{}, errs[0]) - }) } func TestCheckInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { @@ -2618,40 +2117,6 @@ func TestCheckInterfaceDefaultImplementationConcreteTypeUsage(t *testing.T) { `) require.NoError(t, err) }) - - t.Run("type requirement", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface IA { - - struct X { - let x: Int - - fun test(): Int { - return self.x - } - } - } - - contract Test: IA { - - struct X { - let x: Int - - init() { - self.x = 0 - } - } - } - - fun test(): Int { - return Test.X().test() - } - `) - require.NoError(t, err) - }) } func TestCheckBadStructInterface(t *testing.T) { @@ -2659,20 +2124,15 @@ func TestCheckBadStructInterface(t *testing.T) { _, err := ParseAndCheck(t, "struct interface foo { contract h : foo { contract h { } contract h { contract h { } } } }") - errs := RequireCheckerErrors(t, err, 12) + errs := RequireCheckerErrors(t, err, 7) assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[1]) - assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[2]) + assert.IsType(t, &sema.RedeclarationError{}, errs[2]) assert.IsType(t, &sema.RedeclarationError{}, errs[3]) - assert.IsType(t, &sema.RedeclarationError{}, errs[4]) - assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[5]) - assert.IsType(t, &sema.RedeclarationError{}, errs[6]) - assert.IsType(t, &sema.RedeclarationError{}, errs[7]) - assert.IsType(t, &sema.RedeclarationError{}, errs[8]) - assert.IsType(t, &sema.RedeclarationError{}, errs[9]) - assert.IsType(t, &sema.CompositeKindMismatchError{}, errs[10]) - assert.IsType(t, &sema.RedeclarationError{}, errs[11]) + assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[4]) + assert.IsType(t, &sema.RedeclarationError{}, errs[5]) + assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[6]) } func TestCheckInterfaceInheritance(t *testing.T) { @@ -3717,244 +3177,6 @@ func TestCheckInterfaceTypeDefinitionInheritance(t *testing.T) { t.Parallel() - t.Run("type requirement", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface A { - struct Nested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B: A {} - - contract interface C: B {} - - contract X: C { - struct Nested { - access(all) fun test(): Int { - return 3 - } - } - } - `) - - require.NoError(t, err) - }) - - t.Run("type requirement negative", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface A { - struct Nested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B: A {} - - contract interface C: B {} - - contract X: C {} - `) - - errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.ConformanceError{}, errs[0]) - }) - - t.Run("type requirement wrong entitlement", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - entitlement E - - contract interface A { - struct Nested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B: A {} - - contract interface C: B {} - - contract X: C { - struct Nested { - access(E) fun test(): Int { - return 3 - } - } - } - `) - - errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.ConformanceError{}, errs[0]) - }) - - t.Run("type requirement multiple", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface A { - struct ANested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B { - struct BNested { - access(all) fun test(): Int { - return 4 - } - } - } - - contract interface C: A, B {} - - contract X: C { - struct ANested { - access(all) fun test(): Int { - return 3 - } - } - - struct BNested { - access(all) fun test(): Int { - return 3 - } - } - } - `) - - require.NoError(t, err) - }) - - t.Run("type requirement multiple not conforming", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface A { - struct ANested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B { - struct BNested { - access(all) fun test(): Int { - return 4 - } - } - } - - contract interface C: A, B {} - - contract X: C { - struct ANested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract Y: C { - struct BNested { - access(all) fun test(): Int { - return 3 - } - } - } - `) - - errs := RequireCheckerErrors(t, err, 2) - - conformanceError := &sema.ConformanceError{} - require.ErrorAs(t, errs[0], &conformanceError) - assert.Empty(t, conformanceError.MissingMembers) - assert.Len(t, conformanceError.MissingNestedCompositeTypes, 1) - assert.Equal(t, conformanceError.MissingNestedCompositeTypes[0].Identifier, "BNested") - - require.ErrorAs(t, errs[1], &conformanceError) - assert.Empty(t, conformanceError.MissingMembers) - assert.Len(t, conformanceError.MissingNestedCompositeTypes, 1) - assert.Equal(t, conformanceError.MissingNestedCompositeTypes[0].Identifier, "ANested") - }) - - t.Run("nested struct conflicting", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface A { - struct Nested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B: A { - struct Nested { - access(all) fun test(): String { - return "three" - } - } - } - `) - - errs := RequireCheckerErrors(t, err, 1) - memberConflictError := &sema.InterfaceMemberConflictError{} - require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, common.DeclarationKindStructure, memberConflictError.MemberKind) - assert.Equal(t, common.DeclarationKindStructure, memberConflictError.ConflictingMemberKind) - }) - - t.Run("nested identical struct conflict", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface A { - struct Nested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B: A { - struct Nested { - access(all) fun test(): Int { - return 3 - } - } - } - `) - - errs := RequireCheckerErrors(t, err, 1) - memberConflictError := &sema.InterfaceMemberConflictError{} - require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, common.DeclarationKindStructure, memberConflictError.MemberKind) - assert.Equal(t, common.DeclarationKindStructure, memberConflictError.ConflictingMemberKind) - }) - t.Run("nested resource interface conflicting", func(t *testing.T) { t.Parallel() @@ -3977,103 +3199,8 @@ func TestCheckInterfaceTypeDefinitionInheritance(t *testing.T) { } `) - errs := RequireCheckerErrors(t, err, 1) - memberConflictError := &sema.InterfaceMemberConflictError{} - require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, common.DeclarationKindResourceInterface, memberConflictError.MemberKind) - assert.Equal(t, common.DeclarationKindResourceInterface, memberConflictError.ConflictingMemberKind) - }) - - t.Run("nested mixed types conflicting", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface A { - struct interface Nested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B: A { - resource Nested { - access(all) fun test(): String { - return "three" - } - } - } - `) - - errs := RequireCheckerErrors(t, err, 1) - memberConflictError := &sema.InterfaceMemberConflictError{} - require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, common.DeclarationKindStructureInterface, memberConflictError.MemberKind) - assert.Equal(t, common.DeclarationKindResource, memberConflictError.ConflictingMemberKind) - }) - - t.Run("nested struct conflicting indirect", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface A { - struct Nested { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B { - struct Nested { - access(all) fun test(): String { - return "three" - } - } - } - - contract interface C: A, B {} - `) - - errs := RequireCheckerErrors(t, err, 1) - memberConflictError := &sema.InterfaceMemberConflictError{} - require.ErrorAs(t, errs[0], &memberConflictError) - assert.Equal(t, common.DeclarationKindStructure, memberConflictError.MemberKind) - assert.Equal(t, common.DeclarationKindStructure, memberConflictError.ConflictingMemberKind) - }) - - t.Run("nested type requirement", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface A { - struct NestedA { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B { - struct NestedB { - access(all) fun test(): String { - return "three" - } - } - } - - contract interface C: A, B {} - - contract D: C {} - `) - - errs := RequireCheckerErrors(t, err, 2) - conformanceError := &sema.ConformanceError{} - require.ErrorAs(t, errs[0], &conformanceError) - require.ErrorAs(t, errs[1], &conformanceError) + // A.Nested and B.Nested are two distinct separate functions + require.NoError(t, err) }) t.Run("nested interface inheritance", func(t *testing.T) { diff --git a/runtime/tests/checker/intersection_test.go b/runtime/tests/checker/intersection_test.go index 5ace34afdc..d3395b740c 100644 --- a/runtime/tests/checker/intersection_test.go +++ b/runtime/tests/checker/intersection_test.go @@ -264,36 +264,6 @@ func TestCheckIntersectionType(t *testing.T) { assert.IsType(t, &sema.AmbiguousIntersectionTypeError{}, errs[0]) }) - - t.Run("intersection type requirement", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - contract interface CI { - resource interface RI {} - - resource R: RI {} - - fun createR(): @R - } - - contract C: CI { - resource R: CI.RI {} - - fun createR(): @R { - return <- create R() - } - } - - fun test() { - let r <- C.createR() - let r2: @{CI.RI} <- r - destroy r2 - } - `) - require.NoError(t, err) - }) } func TestCheckIntersectionTypeMemberAccess(t *testing.T) { diff --git a/runtime/tests/checker/nesting_test.go b/runtime/tests/checker/nesting_test.go index 4f235ee487..273915aa34 100644 --- a/runtime/tests/checker/nesting_test.go +++ b/runtime/tests/checker/nesting_test.go @@ -103,13 +103,21 @@ func TestCheckCompositeDeclarationNesting(t *testing.T) { assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) + case common.CompositeKindEvent: + require.NoError(t, err) + case common.CompositeKindResource, common.CompositeKindStructure, - common.CompositeKindEvent, common.CompositeKindAttachment, common.CompositeKindEnum: - require.NoError(t, err) + if outerIsInterface && !innerIsInterface { + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.InvalidNestedDeclarationError{}, errs[0]) + } else { + require.NoError(t, err) + } default: t.Errorf("unknown outer composite kind %s", outerComposite) diff --git a/runtime/tests/checker/reference_test.go b/runtime/tests/checker/reference_test.go index 809a461135..59ca11315b 100644 --- a/runtime/tests/checker/reference_test.go +++ b/runtime/tests/checker/reference_test.go @@ -1370,57 +1370,6 @@ func TestCheckArrayAccessReference(t *testing.T) { require.NoError(t, err) } -func TestCheckReferenceTypeImplicitConformance(t *testing.T) { - - t.Parallel() - - t.Run("valid", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - - contract interface CI { - struct S {} - } - - contract C: CI { - struct S {} - } - - let s = C.S() - - let refS: &CI.S = &s as &C.S - `) - - require.NoError(t, err) - }) - - t.Run("invalid", func(t *testing.T) { - - t.Parallel() - - _, err := ParseAndCheck(t, ` - - contract interface CI { - struct S {} - } - - contract C { - struct S {} - } - - let s = C.S() - - let refS: &CI.S = &s as &C.S - `) - - errs := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) - }) -} - func TestCheckInvalidatedReferenceUse(t *testing.T) { t.Parallel() diff --git a/runtime/tests/checker/resources_test.go b/runtime/tests/checker/resources_test.go index 249b7f3d4b..3dc56de3aa 100644 --- a/runtime/tests/checker/resources_test.go +++ b/runtime/tests/checker/resources_test.go @@ -4680,29 +4680,6 @@ func TestCheckInvalidResourceOptionalBindingFailableCastMissingElse(t *testing.T assert.IsType(t, &sema.ResourceLossError{}, errs[0]) }) - - t.Run("contract interface resource to contract to resource", func(t *testing.T) { - - _, err := ParseAndCheck(t, ` - contract interface CI { - resource R {} - } - - contract C: CI { - resource R {} - } - - fun test(r: @CI.R) { - if let r2 <- r as? @C.R { - destroy r2 - } - } - `) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.ResourceLossError{}, errs[0]) - }) } func TestCheckInvalidResourceFailableCastOutsideOptionalBinding(t *testing.T) { diff --git a/runtime/tests/interpreter/condition_test.go b/runtime/tests/interpreter/condition_test.go index 0852d56955..41ed63b527 100644 --- a/runtime/tests/interpreter/condition_test.go +++ b/runtime/tests/interpreter/condition_test.go @@ -983,141 +983,6 @@ func TestInterpretInitializerWithInterfacePreCondition(t *testing.T) { } } -func TestInterpretTypeRequirementWithPreCondition(t *testing.T) { - - t.Parallel() - - var events []testEvent - - inter, err := parseCheckAndInterpretWithOptions(t, - ` - event AlsoPre(x: Int) - - access(all) struct interface Also { - access(all) fun test(x: Int) { - pre { - x >= 0: "x >= 0" - emit AlsoPre(x: x) - } - } - } - - event ReqPre(x: Int) - - access(all) contract interface Test { - - access(all) struct Nested { - access(all) fun test(x: Int) { - pre { - x >= 1: "x >= 1" - emit ReqPre(x: x) - } - } - } - } - - event ImplPre(x: Int) - - access(all) contract TestImpl: Test { - - access(all) struct Nested: Also { - access(all) fun test(x: Int) { - pre { - x < 2: "x < 2" - emit ImplPre(x: x) - } - } - } - } - - access(all) fun test(x: Int) { - TestImpl.Nested().test(x: x) - } - `, - ParseCheckAndInterpretOptions{ - Config: &interpreter.Config{ - ContractValueHandler: makeContractValueHandler(nil, nil, nil), - OnEventEmitted: func( - _ *interpreter.Interpreter, - _ interpreter.LocationRange, - event *interpreter.CompositeValue, - eventType *sema.CompositeType, - ) error { - events = append(events, testEvent{ - event: event, - eventType: eventType, - }) - return nil - }, - }, - }, - ) - require.NoError(t, err) - - t.Run("-1", func(t *testing.T) { - events = nil - - _, err := inter.Invoke("test", interpreter.NewUnmeteredIntValueFromInt64(-1)) - RequireError(t, err) - - var conditionErr interpreter.ConditionError - require.ErrorAs(t, err, &conditionErr) - - // NOTE: The type requirement condition (`Test.Nested`) is evaluated first, - // before the type's conformances (`Also`) - - assert.Equal(t, "x >= 1", conditionErr.Message) - - require.Len(t, events, 0) - }) - - t.Run("0", func(t *testing.T) { - events = nil - - _, err := inter.Invoke("test", interpreter.NewUnmeteredIntValueFromInt64(0)) - RequireError(t, err) - - var conditionErr interpreter.ConditionError - require.ErrorAs(t, err, &conditionErr) - - assert.Equal(t, "x >= 1", conditionErr.Message) - - require.Len(t, events, 0) - }) - - t.Run("1", func(t *testing.T) { - events = nil - - value, err := inter.Invoke("test", interpreter.NewUnmeteredIntValueFromInt64(1)) - require.NoError(t, err) - - assert.IsType(t, - interpreter.Void, - value, - ) - - require.Len(t, events, 3) - }) - - t.Run("2", func(t *testing.T) { - events = nil - - _, err := inter.Invoke("test", interpreter.NewUnmeteredIntValueFromInt64(2)) - require.IsType(t, - interpreter.Error{}, - err, - ) - interpreterErr := err.(interpreter.Error) - - require.IsType(t, - interpreter.ConditionError{}, - interpreterErr.Err, - ) - - require.Len(t, events, 2) - }) -} - func TestInterpretResourceInterfaceInitializerAndDestructorPreConditions(t *testing.T) { t.Parallel() @@ -1225,158 +1090,6 @@ func TestInterpretResourceInterfaceInitializerAndDestructorPreConditions(t *test }) } -func TestInterpretResourceTypeRequirementInitializerAndDestructorPreConditions(t *testing.T) { - - t.Parallel() - - newInterpreter := func(t *testing.T) (inter *interpreter.Interpreter, getEvents func() []testEvent) { - var events []testEvent - - var err error - inter, err = parseCheckAndInterpretWithOptions(t, ` - access(all) - event InitPre(x: Int) - - access(all) - event DestroyPre(x: Int) - - access(all) - contract interface CI { - - access(all) - resource R { - - access(all) - x: Int - - init(_ x: Int) { - pre { - x > 1: "invalid init" - emit InitPre(x: x) - } - } - - destroy() { - pre { - self.x < 3: "invalid destroy" - emit DestroyPre(x: self.x) - } - } - } - } - - access(all) - contract C: CI { - - access(all) - resource R { - - access(all) - let x: Int - - init(_ x: Int) { - self.x = x - } - } - - access(all) - fun test(_ x: Int) { - let r <- create C.R(x) - destroy r - } - } - - access(all) - fun test(_ x: Int) { - C.test(x) - } - `, - ParseCheckAndInterpretOptions{ - Config: &interpreter.Config{ - ContractValueHandler: makeContractValueHandler(nil, nil, nil), - OnEventEmitted: func( - _ *interpreter.Interpreter, - _ interpreter.LocationRange, - event *interpreter.CompositeValue, - eventType *sema.CompositeType, - ) error { - events = append(events, testEvent{ - event: event, - eventType: eventType, - }) - return nil - }, - }, - }, - ) - require.NoError(t, err) - - getEvents = func() []testEvent { - return events - } - - return - } - - t.Run("1", func(t *testing.T) { - t.Parallel() - - inter, getEvents := newInterpreter(t) - _, err := inter.Invoke("test", interpreter.NewUnmeteredIntValueFromInt64(1)) - RequireError(t, err) - - require.IsType(t, - interpreter.Error{}, - err, - ) - interpreterErr := err.(interpreter.Error) - - require.IsType(t, - interpreter.ConditionError{}, - interpreterErr.Err, - ) - conditionError := interpreterErr.Err.(interpreter.ConditionError) - - assert.Equal(t, "invalid init", conditionError.Message) - - require.Len(t, getEvents(), 0) - }) - - t.Run("2", func(t *testing.T) { - t.Parallel() - - inter, getEvents := newInterpreter(t) - _, err := inter.Invoke("test", interpreter.NewUnmeteredIntValueFromInt64(2)) - require.NoError(t, err) - - require.Len(t, getEvents(), 2) - }) - - t.Run("3", func(t *testing.T) { - t.Parallel() - - inter, getEvents := newInterpreter(t) - _, err := inter.Invoke("test", interpreter.NewUnmeteredIntValueFromInt64(3)) - RequireError(t, err) - - require.IsType(t, - interpreter.Error{}, - err, - ) - interpreterErr := err.(interpreter.Error) - - require.IsType(t, - interpreter.ConditionError{}, - interpreterErr.Err, - ) - conditionError := interpreterErr.Err.(interpreter.ConditionError) - - assert.Equal(t, "invalid destroy", conditionError.Message) - - require.Len(t, getEvents(), 1) - }) -} - func TestInterpretFunctionPostConditionInInterface(t *testing.T) { t.Parallel() diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 7e8713f2a9..6c20c02778 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -65,47 +65,6 @@ func TestInterpretInterfaceDefaultImplementation(t *testing.T) { ) }) - t.Run("type requirement", func(t *testing.T) { - - t.Parallel() - - inter, err := parseCheckAndInterpretWithOptions(t, ` - - contract interface IA { - - struct X { - fun test(): Int { - return 42 - } - } - } - - contract Test: IA { - struct X { - } - } - - fun main(): Int { - return Test.X().test() - } - `, - ParseCheckAndInterpretOptions{ - Config: &interpreter.Config{ - ContractValueHandler: makeContractValueHandler(nil, nil, nil), - }, - }, - ) - require.NoError(t, err) - - value, err := inter.Invoke("main") - require.NoError(t, err) - - assert.Equal(t, - interpreter.NewUnmeteredIntValueFromInt64(42), - value, - ) - }) - t.Run("interface variable", func(t *testing.T) { t.Parallel() @@ -198,52 +157,6 @@ func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { ) }) - 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{ - Config: &interpreter.Config{ - ContractValueHandler: makeContractValueHandler(nil, nil, nil), - }, - }, - ) - - require.NoError(t, err) - - value, err := inter.Invoke("main") - require.NoError(t, err) - - assert.Equal(t, - interpreter.NewUnmeteredIntValueFromInt64(42), - value, - ) - }) - } func TestInterpretInterfaceInheritance(t *testing.T) { @@ -427,64 +340,6 @@ func TestInterpretInterfaceInheritance(t *testing.T) { value, ) }) - - t.Run("type requirement", func(t *testing.T) { - - t.Parallel() - - inter, err := parseCheckAndInterpretWithOptions(t, ` - contract interface A { - struct NestedA { - access(all) fun test(): Int { - return 3 - } - } - } - - contract interface B { - struct NestedB { - access(all) fun test(): String { - return "three" - } - } - } - - contract interface C: A, B {} - - contract D: C { - struct NestedA {} - - struct NestedB {} - - access(all) fun getNestedA(): NestedA { - return NestedA() - } - - access(all) fun getNestedB(): NestedB { - return NestedB() - } - } - - access(all) fun main(): Int { - return D.getNestedA().test() - }`, - - ParseCheckAndInterpretOptions{ - Config: &interpreter.Config{ - ContractValueHandler: makeContractValueHandler(nil, nil, nil), - }, - }, - ) - require.NoError(t, err) - - value, err := inter.Invoke("main") - require.NoError(t, err) - - assert.Equal(t, - interpreter.NewUnmeteredIntValueFromInt64(3), - value, - ) - }) } func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { @@ -882,4 +737,91 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { // The post-conditions of the interfaces are executed after that, with the reversed depth-first pre-order. assert.Equal(t, []string{"A", "D", "F", "E", "C", "B"}, logs) }) + + t.Run("nested resource interface unrelated", func(t *testing.T) { + + t.Parallel() + + logFunctionType := sema.NewSimpleFunctionType( + sema.FunctionPurityView, + []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.AnyStructTypeAnnotation, + }, + }, + sema.VoidTypeAnnotation, + ) + + var logs []string + valueDeclaration := stdlib.NewStandardLibraryFunction( + "log", + logFunctionType, + "", + func(invocation interpreter.Invocation) interpreter.Value { + msg := invocation.Arguments[0].(*interpreter.StringValue).Str + logs = append(logs, msg) + return interpreter.Void + }, + ) + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(valueDeclaration) + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, valueDeclaration) + + inter, err := parseCheckAndInterpretWithOptions(t, ` + contract interface A { + struct interface Nested { + access(all) fun test(): Int { + post { print("A") } + } + } + } + + contract interface B: A { + struct interface Nested { + access(all) fun test(): String { + post { print("B") } + } + } + } + + contract C { + struct Nested: B.Nested { + fun test(): String { + return "C" + } + } + } + + access(all) view fun print(_ msg: String): Bool { + log(msg) + return true + } + + access(all) fun main() { + let n = C.Nested() + n.test() + } + `, + ParseCheckAndInterpretOptions{ + CheckerConfig: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + Config: &interpreter.Config{ + BaseActivation: baseActivation, + ContractValueHandler: makeContractValueHandler(nil, nil, nil), + }, + }, + ) + require.NoError(t, err) + + _, err = inter.Invoke("main") + require.NoError(t, err) + + // A.Nested and B.Nested are two distinct separate functions + assert.Equal(t, []string{"B"}, logs) + }) } diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index a24e7873ef..05df4e49f0 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -10765,41 +10765,4 @@ func TestInterpretConditionsWrapperFunctionType(t *testing.T) { _, err := inter.Invoke("test") require.NoError(t, err) }) - - t.Run("type requirement", func(t *testing.T) { - - t.Parallel() - - inter, err := parseCheckAndInterpretWithOptions(t, - ` - contract interface CI { - struct S { - fun test(x: Int) { - pre { true } - } - } - } - - contract C: CI { - struct S { - fun test(x: Int) {} - } - } - - fun test(): fun (Int): Void { - let s = C.S() - return s.test - } - `, - ParseCheckAndInterpretOptions{ - Config: &interpreter.Config{ - ContractValueHandler: makeContractValueHandler(nil, nil, nil), - }, - }, - ) - require.NoError(t, err) - - _, err = inter.Invoke("test") - require.NoError(t, err) - }) } diff --git a/runtime/tests/interpreter/transfer_test.go b/runtime/tests/interpreter/transfer_test.go index 36702a8394..a7b4b5a4fd 100644 --- a/runtime/tests/interpreter/transfer_test.go +++ b/runtime/tests/interpreter/transfer_test.go @@ -100,10 +100,6 @@ func TestInterpretTransferCheck(t *testing.T) { ` contract interface CI { resource interface RI {} - - resource R: RI {} - - fun createR(): @R } contract C: CI { @@ -116,7 +112,7 @@ func TestInterpretTransferCheck(t *testing.T) { fun test() { let r <- C.createR() - let r2: @CI.R <- r as @CI.R + let r2: @C.R <- r as @C.R let r3: @{CI.RI} <- r2 destroy r3 } @@ -141,10 +137,6 @@ func TestInterpretTransferCheck(t *testing.T) { ` contract interface CI { resource interface RI {} - - resource R: RI {} - - fun createR(): @R } contract C: CI { @@ -157,7 +149,7 @@ func TestInterpretTransferCheck(t *testing.T) { fun test() { let r <- C.createR() - let ref: &CI.R = &r as &CI.R + let ref: &C.R = &r as &C.R let intersectionRef: &{CI.RI} = ref destroy r }