From 4de93be982d7be45ebb7a3785bfa9c6054598726 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Nov 2025 10:16:38 -0800 Subject: [PATCH 1/7] Run the subtype checking tests also with static-types --- interpreter/statictype.go | 17 +- interpreter/subtype_check.gen.go | 6 +- runtime/subtype_check_test.go | 1731 +++++++++++++++++++++++++++ sema/subtype_check.gen.go | 2 +- sema/subtype_check_test.go | 1882 ------------------------------ sema/type.go | 8 +- tools/subtype-gen/constants.go | 2 +- 7 files changed, 1750 insertions(+), 1898 deletions(-) create mode 100644 runtime/subtype_check_test.go delete mode 100644 sema/subtype_check_test.go diff --git a/interpreter/statictype.go b/interpreter/statictype.go index 0713a2a962..79e1912251 100644 --- a/interpreter/statictype.go +++ b/interpreter/statictype.go @@ -357,7 +357,7 @@ func (t InclusiveRangeStaticType) BaseType() StaticType { if t.ElementType == nil { return nil } - return &InclusiveRangeStaticType{} + return InclusiveRangeStaticType{} } func (t InclusiveRangeStaticType) TypeArguments() []StaticType { @@ -1350,12 +1350,15 @@ func ConvertStaticToSemaType( ), nil case InclusiveRangeStaticType: - elementType, err := ConvertStaticToSemaType( - context, - t.ElementType, - ) - if err != nil { - return nil, err + var elementType sema.Type + if t.ElementType != nil { + elementType, err = ConvertStaticToSemaType( + context, + t.ElementType, + ) + if err != nil { + return nil, err + } } return sema.NewInclusiveRangeType( diff --git a/interpreter/subtype_check.gen.go b/interpreter/subtype_check.gen.go index 012d10bfe5..caef2ec5f3 100644 --- a/interpreter/subtype_check.gen.go +++ b/interpreter/subtype_check.gen.go @@ -21,7 +21,7 @@ package interpreter import "github.com/onflow/cadence/sema" -func checkSubTypeWithoutEquality_gen(typeConverter TypeConverter, subType StaticType, superType StaticType) bool { +func CheckSubTypeWithoutEquality_gen(typeConverter TypeConverter, subType StaticType, superType StaticType) bool { if subType == PrimitiveStaticTypeNever { return true } @@ -72,8 +72,8 @@ func checkSubTypeWithoutEquality_gen(typeConverter TypeConverter, subType Static IsSubType(typeConverter, subType, PrimitiveStaticTypeFixedPoint) case PrimitiveStaticTypeSignedNumber: - return subType == // TODO: Maybe remove since these predicates only need to check for strict-subtyping, without the "equality". - PrimitiveStaticTypeSignedNumber || + return subType ==// TODO: Maybe remove since these predicates only need to check for strict-subtyping, without the "equality". + PrimitiveStaticTypeSignedNumber || (IsSubType(typeConverter, subType, PrimitiveStaticTypeSignedInteger) || IsSubType(typeConverter, subType, PrimitiveStaticTypeSignedFixedPoint)) diff --git a/runtime/subtype_check_test.go b/runtime/subtype_check_test.go new file mode 100644 index 0000000000..110464a3fb --- /dev/null +++ b/runtime/subtype_check_test.go @@ -0,0 +1,1731 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package runtime + +import ( + "testing" + + "github.com/onflow/cadence/interpreter" + "github.com/onflow/cadence/sema" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence/common" +) + +var location = common.NewStringLocation(nil, "test") + +var interfaceType1 = &sema.InterfaceType{ + Location: location, + Identifier: "I1", + CompositeKind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, +} + +var interfaceType2 = &sema.InterfaceType{ + Location: location, + Identifier: "I2", + CompositeKind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, +} + +var interfaceType3 = &sema.InterfaceType{ + Location: location, + Identifier: "I3", + CompositeKind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, +} + +var interfaceTypeWithConformance1 = &sema.InterfaceType{ + Location: location, + Identifier: "I4", + CompositeKind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceType1}, +} + +var entitlementType = sema.NewEntitlementType( + nil, + common.NewStringLocation(nil, "test"), + "E", +) + +var structType1 = &sema.CompositeType{ + Location: location, + Identifier: "S1", + Kind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, +} + +var structType2 = &sema.CompositeType{ + Location: location, + Identifier: "S2", + Kind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, +} + +var structTypeWithConformance1 = &sema.CompositeType{ + Location: location, + Identifier: "S3", + Kind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceType1}, +} + +var structTypeWithConformance2 = &sema.CompositeType{ + Location: location, + Identifier: "S4", + Kind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceType1, interfaceType2}, +} + +var resourceType1 = &sema.CompositeType{ + Location: location, + Identifier: "R1", + Kind: common.CompositeKindResource, + Members: &sema.StringMemberOrderedMap{}, +} + +var resourceTypeWithConformance1 = &sema.CompositeType{ + Location: location, + Identifier: "R2", + Kind: common.CompositeKindResource, + Members: &sema.StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceType1}, +} + +var resourceTypeWithConformance2 = &sema.CompositeType{ + Location: location, + Identifier: "R3", + Kind: common.CompositeKindResource, + Members: &sema.StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceType1, interfaceType2}, +} + +// checkSubTypeFunctions calls both checkSubTypeWithoutEquality and checkSubTypeWithoutEquality_gen +// and asserts they produce the same result +func checkSubTypeFunctions(t *testing.T, subType sema.Type, superType sema.Type) bool { + result := sema.CheckSubTypeWithoutEquality(subType, superType) + + generatedSemaResult := sema.CheckSubTypeWithoutEquality_gen(subType, superType) + assert.Equal( + t, + result, + generatedSemaResult, + "generated function in `sema` package produced different results for"+ + " subType=%s, superType=%s: manual=%s, generated=%s", + subType, + superType, + result, + generatedSemaResult, + ) + + elaboration := sema.NewElaboration(nil) + + elaboration.SetInterfaceType(interfaceType1.ID(), interfaceType1) + elaboration.SetInterfaceType(interfaceType2.ID(), interfaceType2) + elaboration.SetInterfaceType(interfaceType3.ID(), interfaceType3) + elaboration.SetInterfaceType(interfaceTypeWithConformance1.ID(), interfaceTypeWithConformance1) + + elaboration.SetCompositeType(structType1.ID(), structType1) + elaboration.SetCompositeType(structType2.ID(), structType2) + elaboration.SetCompositeType(structTypeWithConformance1.ID(), structTypeWithConformance1) + elaboration.SetCompositeType(structTypeWithConformance2.ID(), structTypeWithConformance2) + + elaboration.SetCompositeType(resourceType1.ID(), resourceType1) + elaboration.SetCompositeType(resourceTypeWithConformance1.ID(), resourceTypeWithConformance1) + elaboration.SetCompositeType(resourceTypeWithConformance2.ID(), resourceTypeWithConformance2) + + elaboration.SetEntitlementType(entitlementType.ID(), entitlementType) + + //addToElaboration := func(typ sema.Type) { + // if compositeType, ok := typ.(*sema.CompositeType); ok { + // elaboration.SetCompositeType(compositeType.ID(), compositeType) + // } + //} + + subStaticType := interpreter.ConvertSemaToStaticType(nil, subType) + //addToElaboration(subType) + + superStaticType := interpreter.ConvertSemaToStaticType(nil, superType) + //addToElaboration(superType) + + inter, err := interpreter.NewInterpreter( + nil, + common.AddressLocation{}, + &interpreter.Config{ + CompositeTypeHandler: func(location common.Location, typeID interpreter.TypeID) *sema.CompositeType { + return elaboration.CompositeType(typeID) + }, + //InterfaceTypeHandler: func(location common.Location, typeID interpreter.TypeID) *sema.InterfaceType { + // return typeMappings[typeID].(*sema.InterfaceType) + //}, + ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { + return interpreter.VirtualImport{ + Elaboration: elaboration, + } + }, + }, + ) + require.NoError(t, err) + + generatedStaticTypeResult := interpreter.CheckSubTypeWithoutEquality_gen( + inter, + subStaticType, + superStaticType, + ) + assert.Equal( + t, + result, + generatedStaticTypeResult, + "generated function in `interpreter` package produced different results for"+ + " subType=%s, superType=%s: manual=%s, generated=%s", + subType, + superType, + result, + generatedSemaResult, + ) + + return result +} + +// TestCheckSubTypeWithoutEquality tests all paths of checkSubTypeWithoutEquality function +func TestCheckSubTypeWithoutEquality(t *testing.T) { + t.Parallel() + + t.Run("sema.NeverType", func(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + superType sema.Type + }{ + {"Never <: Any", sema.AnyType}, + {"Never <: AnyStruct", sema.AnyStructType}, + {"Never <: AnyResource", sema.AnyResourceType}, + {"Never <: Int", sema.IntType}, + {"Never <: String", sema.StringType}, + {"Never <: Bool", sema.BoolType}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.NeverType, tt.superType) + assert.True(t, result, "sema.NeverType should be a subtype of %v", tt.superType) + }) + } + }) + + t.Run("sema.AnyType", func(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + subType sema.Type + }{ + {"Int <: Any", sema.IntType}, + {"String <: Any", sema.StringType}, + {"Bool <: Any", sema.BoolType}, + {"AnyStruct <: Any", sema.AnyStructType}, + {"AnyResource <: Any", sema.AnyResourceType}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, tt.subType, sema.AnyType) + assert.True(t, result, "%v should be a subtype of sema.AnyType", tt.subType) + }) + } + }) + + t.Run("sema.AnyStructType", func(t *testing.T) { + t.Parallel() + + t.Run("struct types are subtypes of AnyStruct", func(t *testing.T) { + tests := []struct { + name string + subType sema.Type + }{ + {"Int <: AnyStruct", sema.IntType}, + {"String <: AnyStruct", sema.StringType}, + {"Bool <: AnyStruct", sema.BoolType}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, tt.subType, sema.AnyStructType) + assert.True(t, result, "%v should be a subtype of sema.AnyStructType", tt.subType) + }) + } + }) + + t.Run("resource types are NOT subtypes of AnyStruct", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.AnyResourceType, sema.AnyStructType) + assert.False(t, result, "AnyResource should NOT be a subtype of AnyStruct") + }) + + t.Run("sema.AnyType is NOT a subtype of AnyStruct", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.AnyType, sema.AnyStructType) + assert.False(t, result, "sema.AnyType should NOT be a subtype of AnyStruct") + }) + }) + + t.Run("sema.AnyResourceType", func(t *testing.T) { + t.Parallel() + + t.Run("resource types are subtypes of AnyResource", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.AnyResourceType, sema.AnyResourceType) + assert.True(t, result, "AnyResource should be a subtype of AnyResource") + }) + + t.Run("struct types are NOT subtypes of AnyResource", func(t *testing.T) { + tests := []sema.Type{ + sema.IntType, + sema.StringType, + sema.BoolType, + sema.AnyStructType, + } + + for _, subType := range tests { + result := checkSubTypeFunctions(t, subType, sema.AnyResourceType) + assert.False(t, result, "%v should NOT be a subtype of AnyResource", subType) + } + }) + }) + + t.Run("AttachmentTypes", func(t *testing.T) { + t.Parallel() + + t.Run("AnyResourceAttachment", func(t *testing.T) { + // Note: Testing with real attachment types would require more setup + // These tests verify the basic structure + t.Run("non-resource is not subtype", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.IntType, sema.AnyResourceAttachmentType) + assert.False(t, result) + }) + + t.Run("struct is not subtype", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.StringType, sema.AnyResourceAttachmentType) + assert.False(t, result) + }) + + t.Run("AnyStruct is not subtype", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.AnyStructType, sema.AnyResourceAttachmentType) + assert.False(t, result) + }) + }) + + t.Run("AnyStructAttachment", func(t *testing.T) { + t.Run("resource is not subtype", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.AnyResourceType, sema.AnyStructAttachmentType) + assert.False(t, result) + }) + + t.Run("non-attachment struct is not subtype", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.IntType, sema.AnyStructAttachmentType) + assert.False(t, result) + }) + + t.Run("AnyResource is not subtype", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.AnyResourceType, sema.AnyStructAttachmentType) + assert.False(t, result) + }) + }) + }) + + t.Run("sema.HashableStructType", func(t *testing.T) { + t.Parallel() + + t.Run("hashable types are subtypes", func(t *testing.T) { + tests := []sema.Type{ + sema.IntType, + sema.StringType, + sema.BoolType, + sema.TheAddressType, + } + + for _, subType := range tests { + result := checkSubTypeFunctions(t, subType, sema.HashableStructType) + assert.True(t, result, "%v should be a subtype of HashableStruct", subType) + } + }) + }) + + t.Run("PathTypes", func(t *testing.T) { + t.Parallel() + + t.Run("PathType", func(t *testing.T) { + t.Run("StoragePath <: Path", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.StoragePathType, sema.PathType) + assert.True(t, result) + }) + + t.Run("PrivatePath <: Path", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.PrivatePathType, sema.PathType) + assert.True(t, result) + }) + + t.Run("PublicPath <: Path", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.PublicPathType, sema.PathType) + assert.True(t, result) + }) + + t.Run("Int is NOT <: Path", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.IntType, sema.PathType) + assert.False(t, result) + }) + }) + + t.Run("CapabilityPathType", func(t *testing.T) { + t.Run("PrivatePath <: CapabilityPath", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.PrivatePathType, sema.CapabilityPathType) + assert.True(t, result) + }) + + t.Run("PublicPath <: CapabilityPath", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.PublicPathType, sema.CapabilityPathType) + assert.True(t, result) + }) + + t.Run("StoragePath is NOT <: CapabilityPath", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.StoragePathType, sema.CapabilityPathType) + assert.False(t, result) + }) + }) + }) + + t.Run("sema.StorableType", func(t *testing.T) { + t.Parallel() + + t.Run("storable types are subtypes", func(t *testing.T) { + tests := []sema.Type{ + sema.IntType, + sema.StringType, + sema.BoolType, + sema.TheAddressType, + } + + for _, subType := range tests { + result := checkSubTypeFunctions(t, subType, sema.StorableType) + assert.True(t, result, "%v should be a subtype of Storable", subType) + } + }) + }) + + t.Run("NumberTypes", func(t *testing.T) { + t.Parallel() + + t.Run("sema.NumberType", func(t *testing.T) { + tests := []struct { + name string + subType sema.Type + expected bool + }{ + {"sema.NumberType <: Number", sema.NumberType, true}, + {"sema.SignedNumberType <: Number", sema.SignedNumberType, true}, + {"Int <: Number", sema.IntType, true}, + {"Int8 <: Number", sema.Int8Type, true}, + {"UInt <: Number", sema.UIntType, true}, + {"Fix64 <: Number", sema.Fix64Type, true}, + {"UFix64 <: Number", sema.UFix64Type, true}, + {"String is NOT <: Number", sema.StringType, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, tt.subType, sema.NumberType) + assert.Equal(t, tt.expected, result) + }) + } + }) + + t.Run("sema.SignedNumberType", func(t *testing.T) { + tests := []struct { + name string + subType sema.Type + expected bool + }{ + {"sema.SignedNumberType <: SignedNumber", sema.SignedNumberType, true}, + {"Int <: SignedNumber", sema.IntType, true}, + {"Int8 <: SignedNumber", sema.Int8Type, true}, + {"Fix64 <: SignedNumber", sema.Fix64Type, true}, + {"UInt is NOT <: SignedNumber", sema.UIntType, false}, + {"UFix64 is NOT <: SignedNumber", sema.UFix64Type, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, tt.subType, sema.SignedNumberType) + assert.Equal(t, tt.expected, result) + }) + } + }) + + t.Run("sema.IntegerType", func(t *testing.T) { + tests := []struct { + name string + subType sema.Type + expected bool + }{ + {"sema.IntegerType <: Integer", sema.IntegerType, true}, + {"sema.SignedIntegerType <: Integer", sema.SignedIntegerType, true}, + {"sema.FixedSizeUnsignedIntegerType <: Integer", sema.FixedSizeUnsignedIntegerType, true}, + {"sema.UIntType <: Integer", sema.UIntType, true}, + {"Int <: Integer", sema.IntType, true}, + {"UInt8 <: Integer", sema.UInt8Type, true}, + {"Fix64 is NOT <: Integer", sema.Fix64Type, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, tt.subType, sema.IntegerType) + assert.Equal(t, tt.expected, result) + }) + } + }) + + t.Run("sema.SignedIntegerType", func(t *testing.T) { + tests := []struct { + name string + subType sema.Type + expected bool + }{ + {"sema.SignedIntegerType <: SignedInteger", sema.SignedIntegerType, true}, + {"Int <: SignedInteger", sema.IntType, true}, + {"Int8 <: SignedInteger", sema.Int8Type, true}, + {"Int16 <: SignedInteger", sema.Int16Type, true}, + {"Int32 <: SignedInteger", sema.Int32Type, true}, + {"Int64 <: SignedInteger", sema.Int64Type, true}, + {"Int128 <: SignedInteger", sema.Int128Type, true}, + {"Int256 <: SignedInteger", sema.Int256Type, true}, + {"UInt is NOT <: SignedInteger", sema.UIntType, false}, + {"UInt8 is NOT <: SignedInteger", sema.UInt8Type, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, tt.subType, sema.SignedIntegerType) + assert.Equal(t, tt.expected, result) + }) + } + }) + + t.Run("sema.FixedSizeUnsignedIntegerType", func(t *testing.T) { + tests := []struct { + name string + subType sema.Type + expected bool + }{ + {"UInt8 <: FixedSizeUnsignedInteger", sema.UInt8Type, true}, + {"UInt16 <: FixedSizeUnsignedInteger", sema.UInt16Type, true}, + {"UInt32 <: FixedSizeUnsignedInteger", sema.UInt32Type, true}, + {"UInt64 <: FixedSizeUnsignedInteger", sema.UInt64Type, true}, + {"UInt128 <: FixedSizeUnsignedInteger", sema.UInt128Type, true}, + {"UInt256 <: FixedSizeUnsignedInteger", sema.UInt256Type, true}, + {"Word8 <: FixedSizeUnsignedInteger", sema.Word8Type, true}, + {"Word16 <: FixedSizeUnsignedInteger", sema.Word16Type, true}, + {"Word32 <: FixedSizeUnsignedInteger", sema.Word32Type, true}, + {"Word64 <: FixedSizeUnsignedInteger", sema.Word64Type, true}, + {"Word128 <: FixedSizeUnsignedInteger", sema.Word128Type, true}, + {"Word256 <: FixedSizeUnsignedInteger", sema.Word256Type, true}, + {"UInt is NOT <: FixedSizeUnsignedInteger", sema.UIntType, false}, + {"Int is NOT <: FixedSizeUnsignedInteger", sema.IntType, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, tt.subType, sema.FixedSizeUnsignedIntegerType) + assert.Equal(t, tt.expected, result) + }) + } + }) + + t.Run("sema.FixedPointType", func(t *testing.T) { + tests := []struct { + name string + subType sema.Type + expected bool + }{ + {"sema.FixedPointType <: FixedPoint", sema.FixedPointType, true}, + {"sema.SignedFixedPointType <: FixedPoint", sema.SignedFixedPointType, true}, + {"UFix64 <: FixedPoint", sema.UFix64Type, true}, + {"UFix128 <: FixedPoint", sema.UFix128Type, true}, + {"Fix64 <: FixedPoint", sema.Fix64Type, true}, + {"Fix128 <: FixedPoint", sema.Fix128Type, true}, + {"Int is NOT <: FixedPoint", sema.IntType, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, tt.subType, sema.FixedPointType) + assert.Equal(t, tt.expected, result) + }) + } + }) + + t.Run("sema.SignedFixedPointType", func(t *testing.T) { + tests := []struct { + name string + subType sema.Type + expected bool + }{ + {"sema.SignedFixedPointType <: SignedFixedPoint", sema.SignedFixedPointType, true}, + {"Fix64 <: SignedFixedPoint", sema.Fix64Type, true}, + {"Fix128 <: SignedFixedPoint", sema.Fix128Type, true}, + {"UFix64 is NOT <: SignedFixedPoint", sema.UFix64Type, false}, + {"UFix128 is NOT <: SignedFixedPoint", sema.UFix128Type, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSubTypeFunctions(t, tt.subType, sema.SignedFixedPointType) + assert.Equal(t, tt.expected, result) + }) + } + }) + }) + + t.Run("sema.OptionalType", func(t *testing.T) { + t.Parallel() + + t.Run("T <: T?", func(t *testing.T) { + optionalInt := &sema.OptionalType{Type: sema.IntType} + result := checkSubTypeFunctions(t, sema.IntType, optionalInt) + assert.True(t, result, "Int should be a subtype of Int?") + }) + + t.Run("T? <: U? when T <: U", func(t *testing.T) { + optionalNumber := &sema.OptionalType{Type: sema.NumberType} + optionalInt := &sema.OptionalType{Type: sema.IntType} + result := checkSubTypeFunctions(t, optionalInt, optionalNumber) + assert.True(t, result, "Int? should be a subtype of Number?") + }) + + t.Run("T? is NOT <: U? when T is NOT <: U", func(t *testing.T) { + optionalInt := &sema.OptionalType{Type: sema.IntType} + optionalString := &sema.OptionalType{Type: sema.StringType} + result := checkSubTypeFunctions(t, optionalInt, optionalString) + assert.False(t, result, "Int? should NOT be a subtype of String?") + }) + + t.Run("nested optionals", func(t *testing.T) { + // Int <: Int?? + doubleOptionalInt := &sema.OptionalType{ + Type: &sema.OptionalType{Type: sema.IntType}, + } + result := checkSubTypeFunctions(t, sema.IntType, doubleOptionalInt) + assert.True(t, result, "Int should be a subtype of Int??") + }) + }) + + t.Run("sema.DictionaryType", func(t *testing.T) { + t.Parallel() + + t.Run("covariant in key and value types", func(t *testing.T) { + dict1 := &sema.DictionaryType{ + KeyType: sema.IntType, + ValueType: sema.IntType, + } + dict2 := &sema.DictionaryType{ + KeyType: sema.NumberType, + ValueType: sema.NumberType, + } + result := checkSubTypeFunctions(t, dict1, dict2) + assert.True(t, result, "{Int: Int} should be a subtype of {Number: Number}") + }) + + t.Run("not subtype when key types don't match", func(t *testing.T) { + dict1 := &sema.DictionaryType{ + KeyType: sema.IntType, + ValueType: sema.IntType, + } + dict2 := &sema.DictionaryType{ + KeyType: sema.StringType, + ValueType: sema.IntType, + } + result := checkSubTypeFunctions(t, dict1, dict2) + assert.False(t, result, "{Int: Int} should NOT be a subtype of {String: Int}") + }) + + t.Run("not subtype when value types don't match", func(t *testing.T) { + dict1 := &sema.DictionaryType{ + KeyType: sema.IntType, + ValueType: sema.IntType, + } + dict2 := &sema.DictionaryType{ + KeyType: sema.IntType, + ValueType: sema.StringType, + } + result := checkSubTypeFunctions(t, dict1, dict2) + assert.False(t, result, "{Int: Int} should NOT be a subtype of {Int: String}") + }) + + t.Run("non-dictionary is not subtype", func(t *testing.T) { + dict := &sema.DictionaryType{ + KeyType: sema.IntType, + ValueType: sema.StringType, + } + result := checkSubTypeFunctions(t, sema.IntType, dict) + assert.False(t, result, "Int should NOT be a subtype of {Int: String}") + }) + + t.Run("dictionary with optional values", func(t *testing.T) { + dict1 := &sema.DictionaryType{ + KeyType: sema.StringType, + ValueType: sema.IntType, + } + dict2 := &sema.DictionaryType{ + KeyType: sema.StringType, + ValueType: &sema.OptionalType{Type: sema.IntType}, + } + result := checkSubTypeFunctions(t, dict1, dict2) + assert.True(t, result, "{String: Int} should be a subtype of {String: Int?}") + }) + }) + + t.Run("sema.VariableSizedType", func(t *testing.T) { + t.Parallel() + + t.Run("covariant in element type", func(t *testing.T) { + arr1 := &sema.VariableSizedType{Type: sema.IntType} + arr2 := &sema.VariableSizedType{Type: sema.NumberType} + result := checkSubTypeFunctions(t, arr1, arr2) + assert.True(t, result, "[Int] should be a subtype of [Number]") + }) + + t.Run("not subtype when element types don't match", func(t *testing.T) { + arr1 := &sema.VariableSizedType{Type: sema.IntType} + arr2 := &sema.VariableSizedType{Type: sema.StringType} + result := checkSubTypeFunctions(t, arr1, arr2) + assert.False(t, result, "[Int] should NOT be a subtype of [String]") + }) + + t.Run("non-array is not subtype", func(t *testing.T) { + arr := &sema.VariableSizedType{Type: sema.IntType} + result := checkSubTypeFunctions(t, sema.IntType, arr) + assert.False(t, result, "Int should NOT be a subtype of [Int]") + }) + + t.Run("nested arrays", func(t *testing.T) { + // [[Int]] <: [[Number]] + arrInt := &sema.VariableSizedType{ + Type: &sema.VariableSizedType{Type: sema.IntType}, + } + arrNumber := &sema.VariableSizedType{ + Type: &sema.VariableSizedType{Type: sema.NumberType}, + } + result := checkSubTypeFunctions(t, arrInt, arrNumber) + assert.True(t, result, "[[Int]] should be a subtype of [[Number]]") + }) + }) + + t.Run("sema.ConstantSizedType", func(t *testing.T) { + t.Parallel() + + t.Run("covariant in element type with same size", func(t *testing.T) { + arr1 := &sema.ConstantSizedType{Type: sema.IntType, Size: 5} + arr2 := &sema.ConstantSizedType{Type: sema.NumberType, Size: 5} + result := checkSubTypeFunctions(t, arr1, arr2) + assert.True(t, result, "[Int; 5] should be a subtype of [Number; 5]") + }) + + t.Run("not subtype when sizes differ", func(t *testing.T) { + arr1 := &sema.ConstantSizedType{Type: sema.IntType, Size: 5} + arr2 := &sema.ConstantSizedType{Type: sema.IntType, Size: 10} + result := checkSubTypeFunctions(t, arr1, arr2) + assert.False(t, result, "[Int; 5] should NOT be a subtype of [Int; 10]") + }) + + t.Run("not subtype when element types don't match", func(t *testing.T) { + arr1 := &sema.ConstantSizedType{Type: sema.IntType, Size: 5} + arr2 := &sema.ConstantSizedType{Type: sema.StringType, Size: 5} + result := checkSubTypeFunctions(t, arr1, arr2) + assert.False(t, result, "[Int; 5] should NOT be a subtype of [String; 5]") + }) + + t.Run("non-array is not subtype", func(t *testing.T) { + arr := &sema.ConstantSizedType{Type: sema.IntType, Size: 5} + result := checkSubTypeFunctions(t, sema.IntType, arr) + assert.False(t, result, "Int should NOT be a subtype of [Int; 5]") + }) + }) + + t.Run("sema.ReferenceType", func(t *testing.T) { + t.Parallel() + + t.Run("covariant in referenced type with compatible authorization", func(t *testing.T) { + ref1 := &sema.ReferenceType{ + Type: sema.IntType, + Authorization: sema.UnauthorizedAccess, + } + ref2 := &sema.ReferenceType{ + Type: sema.NumberType, + Authorization: sema.UnauthorizedAccess, + } + result := checkSubTypeFunctions(t, ref1, ref2) + assert.True(t, result, "&Int should be a subtype of &Number") + }) + + t.Run("not subtype when authorization doesn't permit", func(t *testing.T) { + auth := sema.NewEntitlementSetAccess( + []*sema.EntitlementType{entitlementType}, + sema.Disjunction, + ) + + ref1 := &sema.ReferenceType{ + Type: sema.IntType, + Authorization: sema.UnauthorizedAccess, + } + ref2 := &sema.ReferenceType{ + Type: sema.IntType, + Authorization: auth, + } + result := checkSubTypeFunctions(t, ref1, ref2) + assert.False(t, result, "unauthorized reference should NOT be a subtype of authorized reference") + }) + + t.Run("not subtype when referenced types don't match", func(t *testing.T) { + ref1 := &sema.ReferenceType{ + Type: sema.IntType, + Authorization: sema.UnauthorizedAccess, + } + ref2 := &sema.ReferenceType{ + Type: sema.StringType, + Authorization: sema.UnauthorizedAccess, + } + result := checkSubTypeFunctions(t, ref1, ref2) + assert.False(t, result, "&Int should NOT be a subtype of &String") + }) + + t.Run("non-reference is not subtype", func(t *testing.T) { + ref := &sema.ReferenceType{ + Type: sema.IntType, + Authorization: sema.UnauthorizedAccess, + } + result := checkSubTypeFunctions(t, sema.IntType, ref) + assert.False(t, result, "Int should NOT be a subtype of &Int") + }) + + t.Run("reference to resource type", func(t *testing.T) { + // &AnyResource <: &AnyResource + ref1 := &sema.ReferenceType{ + Type: sema.AnyResourceType, + Authorization: sema.UnauthorizedAccess, + } + ref2 := &sema.ReferenceType{ + Type: sema.AnyResourceType, + Authorization: sema.UnauthorizedAccess, + } + result := checkSubTypeFunctions(t, ref1, ref2) + assert.True(t, result, "&AnyResource should be a subtype of &AnyResource") + }) + + t.Run("reference to optional type", func(t *testing.T) { + // &Int <: &Int? + refToInt := &sema.ReferenceType{ + Type: sema.IntType, + Authorization: sema.UnauthorizedAccess, + } + refToOptInt := &sema.ReferenceType{ + Type: &sema.OptionalType{Type: sema.IntType}, + Authorization: sema.UnauthorizedAccess, + } + result := checkSubTypeFunctions(t, refToInt, refToOptInt) + assert.True(t, result, "&Int should be a subtype of &Int?") + }) + + t.Run("reference with same authorization different types", func(t *testing.T) { + // &String is NOT <: &Int even with same auth + ref1 := &sema.ReferenceType{ + Type: sema.StringType, + Authorization: sema.UnauthorizedAccess, + } + ref2 := &sema.ReferenceType{ + Type: sema.IntType, + Authorization: sema.UnauthorizedAccess, + } + result := checkSubTypeFunctions(t, ref1, ref2) + assert.False(t, result, "&String should NOT be a subtype of &Int") + }) + }) + + t.Run("sema.FunctionType", func(t *testing.T) { + t.Parallel() + + t.Run("view function is subtype of impure function", func(t *testing.T) { + viewFunc := &sema.FunctionType{ + Purity: sema.FunctionPurityView, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + impureFunc := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + result := checkSubTypeFunctions(t, viewFunc, impureFunc) + assert.True(t, result, "view function should be a subtype of impure function") + }) + + t.Run("impure function is NOT subtype of view function", func(t *testing.T) { + viewFunc := &sema.FunctionType{ + Purity: sema.FunctionPurityView, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + impureFunc := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + result := checkSubTypeFunctions(t, impureFunc, viewFunc) + assert.False(t, result, "impure function should NOT be a subtype of view function") + }) + + t.Run("contravariant in parameter types", func(t *testing.T) { + // fun(Number): Int <: fun(Int): Int + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.NumberType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + result := checkSubTypeFunctions(t, func1, func2) + assert.True(t, result, "fun(Number): Int should be a subtype of fun(Int): Int") + }) + + t.Run("covariant in return type", func(t *testing.T) { + // fun(Int): Int <: fun(Int): Number + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.NumberType), + } + result := checkSubTypeFunctions(t, func1, func2) + assert.True(t, result, "fun(Int): Int should be a subtype of fun(Int): Number") + }) + + t.Run("not subtype when parameter contravariance fails", func(t *testing.T) { + // fun(Int): Int is NOT <: fun(Number): Int + // Because Number is NOT <: Int (contravariance requirement fails) + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.NumberType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + result := checkSubTypeFunctions(t, func1, func2) + assert.False(t, result, "fun(Int): Int should NOT be subtype of fun(Number): Int (contravariance fails)") + }) + + t.Run("not subtype when parameter arity differs", func(t *testing.T) { + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + result := checkSubTypeFunctions(t, func1, func2) + assert.False(t, result, "functions with different arities should NOT be subtypes") + }) + + t.Run("not subtype when constructor flags differ", func(t *testing.T) { + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + IsConstructor: true, + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + IsConstructor: false, + } + result := checkSubTypeFunctions(t, func1, func2) + assert.False(t, result, "constructor and non-constructor functions should NOT be subtypes") + }) + + t.Run("non-function is not subtype", func(t *testing.T) { + fn := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + result := checkSubTypeFunctions(t, sema.IntType, fn) + assert.False(t, result, "Int should NOT be a subtype of function") + }) + + t.Run("function with Void return type", func(t *testing.T) { + // fun(Int): Void <: fun(Int): Void + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + } + result := checkSubTypeFunctions(t, func1, func2) + assert.True(t, result, "functions with Void return should be subtypes") + }) + + t.Run("function with different return types", func(t *testing.T) { + // fun(Int): String is NOT <: fun(Int): Int + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.StringType), + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + } + result := checkSubTypeFunctions(t, func1, func2) + assert.False(t, result, "function with String return should NOT be subtype of Int return") + }) + + t.Run("function with different arity", func(t *testing.T) { + // fun() is NOT <: fun(Int) + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + Arity: &sema.Arity{Min: 0, Max: 0}, + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(sema.IntType)}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + Arity: &sema.Arity{Min: 1, Max: 1}, + } + result := checkSubTypeFunctions(t, func1, func2) + assert.False(t, result, "functions with different arity should NOT be subtypes") + }) + + t.Run("function with type parameters not equal", func(t *testing.T) { + // fun() is NOT <: fun() + typeParam1 := &sema.TypeParameter{ + Name: "T", + TypeBound: sema.IntType, + } + typeParam2 := &sema.TypeParameter{ + Name: "T", + TypeBound: sema.StringType, + } + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + TypeParameters: []*sema.TypeParameter{typeParam1}, + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + TypeParameters: []*sema.TypeParameter{typeParam2}, + } + result := checkSubTypeFunctions(t, func1, func2) + assert.False(t, result, "functions with different type parameter bounds should NOT be subtypes") + }) + + t.Run("function with different type parameter count", func(t *testing.T) { + // fun() is NOT <: fun() + typeParam := &sema.TypeParameter{ + Name: "T", + TypeBound: sema.IntType, + } + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + TypeParameters: []*sema.TypeParameter{typeParam}, + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.IntType), + TypeParameters: []*sema.TypeParameter{typeParam, typeParam}, + } + result := checkSubTypeFunctions(t, func1, func2) + assert.False(t, result, "functions with different type parameter count should NOT be subtypes") + }) + + t.Run("function returning array", func(t *testing.T) { + // fun(): [Int] <: fun(): [Number] + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.VariableSizedType{Type: sema.IntType}, + ), + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.VariableSizedType{Type: sema.NumberType}, + ), + } + result := checkSubTypeFunctions(t, func1, func2) + assert.True(t, result, "fun(): [Int] should be subtype of fun(): [Number]") + }) + + t.Run("function with array parameter", func(t *testing.T) { + // fun([Number]): Void <: fun([Int]): Void (contravariance) + func1 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(&sema.VariableSizedType{Type: sema.NumberType})}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.VoidType), + } + func2 := &sema.FunctionType{ + Purity: sema.FunctionPurityImpure, + Parameters: []sema.Parameter{ + {TypeAnnotation: sema.NewTypeAnnotation(&sema.VariableSizedType{Type: sema.IntType})}, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.VoidType), + } + result := checkSubTypeFunctions(t, func1, func2) + assert.True(t, result, "fun([Number]): Void should be subtype of fun([Int]): Void") + }) + }) + + t.Run("IntersectionType", func(t *testing.T) { + t.Parallel() + + t.Run("AnyResource intersection with nil subtype", func(t *testing.T) { + result := checkSubTypeFunctions(t, + sema.AnyResourceType, + &sema.IntersectionType{ + LegacyType: sema.AnyResourceType, + Types: []*sema.InterfaceType{interfaceType1}, + }, + ) + assert.False(t, result, "AnyResource should NOT be a subtype of AnyResource{I}") + }) + + t.Run("AnyStruct intersection with nil subtype", func(t *testing.T) { + result := checkSubTypeFunctions(t, + sema.AnyStructType, + &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{interfaceType1}, + }, + ) + assert.False(t, result, "AnyStruct should NOT be a subtype of AnyStruct{I}") + }) + + t.Run("Any intersection with nil subtype", func(t *testing.T) { + result := checkSubTypeFunctions(t, + sema.AnyType, + &sema.IntersectionType{ + LegacyType: sema.AnyType, + Types: []*sema.InterfaceType{interfaceType1}, + }, + ) + assert.False(t, result, "Any should NOT be a subtype of Any{I}") + }) + + // Tests for sema.IntersectionType subtype with nil sema.LegacyType + t.Run("intersection with nil legacy type as subtype", func(t *testing.T) { + // {I1, I2} <: {I1} when I1 is subset of {I1, I2} + subType := &sema.IntersectionType{ + Types: []*sema.InterfaceType{interfaceType1, interfaceType2}, + } + superType := &sema.IntersectionType{ + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.True(t, result, "{I1, I2} should be a subtype of {I1}") + }) + + t.Run("intersection with nil legacy type not subtype when not subset", func(t *testing.T) { + // {I1} is NOT <: {I2} + subType := &sema.IntersectionType{ + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "{I1} should NOT be a subtype of {I2}") + }) + + // Tests for sema.IntersectionType subtype with AnyResource sema.LegacyType + t.Run("AnyResource intersection subtype with matching interfaces", func(t *testing.T) { + // AnyResource{I1, I2} <: AnyResource{I1} + subType := &sema.IntersectionType{ + LegacyType: sema.AnyResourceType, + Types: []*sema.InterfaceType{interfaceType1, interfaceType2}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyResourceType, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.True(t, result, "AnyResource{I1, I2} should be a subtype of AnyResource{I1}") + }) + + t.Run("AnyResource intersection not subtype of AnyStruct intersection", func(t *testing.T) { + subType := &sema.IntersectionType{ + LegacyType: sema.AnyResourceType, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "AnyResource{I} should NOT be a subtype of AnyStruct{I}") + }) + + // Tests for sema.IntersectionType subtype with sema.CompositeType sema.LegacyType + t.Run("composite intersection subtype when interfaces match", func(t *testing.T) { + + // R{I1} <: AnyResource{} when R conforms to I1 + subType := &sema.IntersectionType{ + LegacyType: resourceTypeWithConformance1, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyResourceType, + Types: []*sema.InterfaceType{}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.True(t, result, "R{I1} should be a subtype of AnyResource{}") + }) + + t.Run("composite intersection not subtype when composite doesn't conform", func(t *testing.T) { + + // R{I2} is NOT <: AnyResource{I1} when R doesn't conform to I1 + subType := &sema.IntersectionType{ + LegacyType: resourceType1, + Types: []*sema.InterfaceType{interfaceType2}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyResourceType, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "R{I2} should NOT be a subtype of AnyResource{I1} when R doesn't conform") + }) + + // Tests for sema.ConformingType (sema.CompositeType) as subtype of sema.IntersectionType + t.Run("composite type subtype of intersection when conforming", func(t *testing.T) { + + // S <: AnyStruct{I1} when S conforms to I1 + superType := &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, structTypeWithConformance1, superType) + assert.True(t, result, "composite should be a subtype of intersection when conforming") + }) + + t.Run("composite type not subtype of intersection when not conforming", func(t *testing.T) { + + // S is NOT <: AnyStruct{I2} when S doesn't conform to I2 + superType := &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, structType1, superType) + assert.False(t, result, "composite should NOT be a subtype of intersection when not conforming") + }) + + t.Run("composite type not subtype when wrong base type", func(t *testing.T) { + // Resource R is NOT <: AnyStruct{I1} even if conforming + superType := &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, resourceTypeWithConformance1, superType) + assert.False(t, result, "resource should NOT be a subtype of struct intersection") + }) + + // Tests for sema.IntersectionType supertype with non-Any* legacy type + t.Run("intersection with composite legacy type", func(t *testing.T) { + // S1{I1} <: S1{I2} when S1 == S1 (owner may restrict/unrestrict) + subType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.True(t, result, "S1{I1} should be a subtype of S1{I2} when same composite") + }) + + t.Run("intersection with different composite legacy types", func(t *testing.T) { + // S1{I1} is NOT <: S2{I1} when S1 != S2 + subType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: structType2, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "S1{I1} should NOT be a subtype of S2{I1} when different composites") + }) + + t.Run("intersection with interface legacy not subtype of composite intersection", func(t *testing.T) { + // I3{I1} is NOT <: S{I2} (interface legacy type vs composite legacy type) + subType := &sema.IntersectionType{ + LegacyType: interfaceType3, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "I3{I1} should NOT be a subtype of S{I2}") + }) + + t.Run("composite type subtype of composite intersection", func(t *testing.T) { + // S <: S{I1} (owner may freely restrict) + superType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, structType1, superType) + assert.True(t, result, "composite should be a subtype of its own intersection type") + }) + + t.Run("intersection nil legacy type not subtype of composite intersection", func(t *testing.T) { + // {I1} is NOT <: S{I1} statically + subType := &sema.IntersectionType{ + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "{I1} should NOT be statically a subtype of S{I1}") + }) + + t.Run("intersection Any* legacy type not subtype of composite intersection", func(t *testing.T) { + // AnyStruct{I1} is NOT <: S{I1} statically + subType := &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "AnyStruct{I1} should NOT be statically a subtype of S{I1}") + }) + + t.Run("sema.AnyResourceType not subtype of composite intersection", func(t *testing.T) { + // AnyResource is NOT <: R{I1} statically + superType := &sema.IntersectionType{ + LegacyType: resourceType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, sema.AnyResourceType, superType) + assert.False(t, result, "AnyResource should NOT be statically a subtype of R{I1}") + }) + + t.Run("sema.AnyStructType not subtype of composite intersection", func(t *testing.T) { + // AnyStruct is NOT <: S{I1} statically + superType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, sema.AnyStructType, superType) + assert.False(t, result, "AnyStruct should NOT be statically a subtype of S{I1}") + }) + + t.Run("sema.AnyType not subtype of composite intersection", func(t *testing.T) { + // Any is NOT <: S{I1} statically + superType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, sema.AnyType, superType) + assert.False(t, result, "Any should NOT be statically a subtype of S{I1}") + }) + + t.Run("optional type not subtype of composite intersection", func(t *testing.T) { + // Int? is NOT <: S{I1} + optionalType := &sema.OptionalType{Type: sema.IntType} + superType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, optionalType, superType) + assert.False(t, result, "Int? should NOT be subtype of S{I1}") + }) + + t.Run("struct composite intersection not subtype of AnyResource intersection", func(t *testing.T) { + // S{I1} is NOT <: AnyResource{I2} because S is not a resource + subType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyResourceType, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "S{I1} should NOT be subtype of AnyResource{I2} (struct vs resource)") + }) + + t.Run("resource composite intersection is subtype of AnyResource intersection with conformance", func(t *testing.T) { + // R{I1} <: AnyResource{I2} if R is a resource and R conforms to I2 + subType := &sema.IntersectionType{ + LegacyType: resourceTypeWithConformance2, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyResourceType, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.True(t, result, "R{I1} should be subtype of AnyResource{I2} when R conforms to I2") + }) + + t.Run("resource composite intersection not subtype of AnyResource without conformance", func(t *testing.T) { + // R{I1} is NOT <: AnyResource{I2} if R doesn't conform to I2 + subType := &sema.IntersectionType{ + LegacyType: resourceTypeWithConformance1, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyResourceType, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "R{I1} should NOT be subtype of AnyResource{I2} when R doesn't conform to I2") + }) + + t.Run("struct composite intersection is subtype of AnyStruct intersection with conformance", func(t *testing.T) { + // S{I1} <: AnyStruct{I2} if S is a struct and S conforms to I2 + subType := &sema.IntersectionType{ + LegacyType: structTypeWithConformance2, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.True(t, result, "S{I1} should be subtype of AnyStruct{I2} when S conforms to I2") + }) + + t.Run("resource composite intersection not subtype of AnyStruct intersection", func(t *testing.T) { + // R{I1} is NOT <: AnyStruct{I2} because R is a resource, not a struct + subType := &sema.IntersectionType{ + LegacyType: resourceType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.False(t, result, "R{I1} should NOT be subtype of AnyStruct{I2} (resource vs struct)") + }) + + t.Run("composite intersection is subtype of sema.AnyType intersection with conformance", func(t *testing.T) { + // S{I1} <: Any{I2} if S conforms to I2 + subType := &sema.IntersectionType{ + LegacyType: structTypeWithConformance2, + Types: []*sema.InterfaceType{interfaceType1}, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyType, + Types: []*sema.InterfaceType{interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, superType) + assert.True(t, result, "S{I1} should be subtype of Any{I2} when S conforms to I2") + }) + + // Test intersection subtype with sema.IntersectionType interface supertype + t.Run("intersection type subtype of interface type", func(t *testing.T) { + // {I1, I2} <: I1 when I1 is in the intersection set + subType := &sema.IntersectionType{ + Types: []*sema.InterfaceType{interfaceType1, interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, interfaceType1) + assert.True(t, result, "{I1, I2} should be a subtype of I1") + }) + + t.Run("intersection type not subtype of interface not in set", func(t *testing.T) { + // {I1, I2} is NOT <: I3 when I3 is not in the intersection set + subType := &sema.IntersectionType{ + Types: []*sema.InterfaceType{interfaceType1, interfaceType2}, + } + result := checkSubTypeFunctions(t, subType, interfaceType3) + assert.False(t, result, "{I1, I2} should NOT be a subtype of I3") + }) + + t.Run("parameterized type not subtype of intersection", func(t *testing.T) { + // Capability is NOT <: {I1} + capType := &sema.CapabilityType{ + BorrowType: sema.IntType, + } + superType := &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{}, + } + result := checkSubTypeFunctions(t, capType, superType) + assert.False(t, result, "Capability should NOT be subtype of {I1}") + }) + }) + + t.Run("CompositeType", func(t *testing.T) { + t.Parallel() + + t.Run("different composite types are not subtypes", func(t *testing.T) { + result := checkSubTypeFunctions(t, structType1, structType2) + assert.False(t, result, "different composite types should NOT be subtypes") + }) + + t.Run("non-composite is not subtype of composite", func(t *testing.T) { + result := checkSubTypeFunctions(t, sema.IntType, structType1) + assert.False(t, result, "Int should NOT be a subtype of composite") + }) + + // Tests for sema.IntersectionType subtype with sema.CompositeType supertype + t.Run("intersection with nil legacy type not subtype of composite", func(t *testing.T) { + // {I} is NOT <: S statically + subType := &sema.IntersectionType{ + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, structType1) + assert.False(t, result, "{I} should NOT be statically a subtype of S") + }) + + t.Run("intersection with Any* legacy type not subtype of composite", func(t *testing.T) { + // AnyStruct{I} is NOT <: S statically + subType := &sema.IntersectionType{ + LegacyType: sema.AnyStructType, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, structType1) + assert.False(t, result, "AnyStruct{I} should NOT be statically a subtype of S") + }) + + t.Run("intersection with matching composite legacy type", func(t *testing.T) { + // S{I} <: S (owner may freely unrestrict) + subType := &sema.IntersectionType{ + LegacyType: structType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, structType1) + assert.True(t, result, "S{I} should be a subtype of S (unrestrict)") + }) + + t.Run("intersection with different composite legacy type", func(t *testing.T) { + // S1{I} is NOT <: S2 when S1 != S2 + subType := &sema.IntersectionType{ + LegacyType: structType2, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, structType1) + assert.False(t, result, "S1{I} should NOT be a subtype of S2") + }) + + t.Run("intersection with interface legacy type not subtype of composite", func(t *testing.T) { + // I{I} is NOT <: S (interface legacy type in intersection) + subType := &sema.IntersectionType{ + LegacyType: interfaceType1, + Types: []*sema.InterfaceType{interfaceType1}, + } + result := checkSubTypeFunctions(t, subType, structType1) + assert.False(t, result, "intersection with interface legacy type should NOT be subtype of composite") + }) + }) + + t.Run("InterfaceType", func(t *testing.T) { + t.Parallel() + + t.Run("composite conforming to interface", func(t *testing.T) { + result := checkSubTypeFunctions(t, structTypeWithConformance1, interfaceType1) + assert.True(t, result, "composite conforming to interface should be a subtype") + }) + + t.Run("composite NOT conforming to interface", func(t *testing.T) { + result := checkSubTypeFunctions(t, structType1, interfaceType1) + assert.False(t, result, "composite NOT conforming to interface should NOT be a subtype") + }) + + t.Run("composite with wrong kind", func(t *testing.T) { + result := checkSubTypeFunctions(t, resourceType1, interfaceType1) + assert.False(t, result, "composite with different kind should NOT be a subtype") + }) + + t.Run("interface subtype of interface", func(t *testing.T) { + result := checkSubTypeFunctions(t, interfaceTypeWithConformance1, interfaceType1) + assert.True(t, result, "interface conforming to another interface should be a subtype") + }) + + t.Run("non-conforming type not subtype of interface", func(t *testing.T) { + // Test fallback to sema.IsParameterizedSubType for types that don't match the specific cases + // Int is NOT <: sema.InterfaceType + result := checkSubTypeFunctions(t, sema.IntType, interfaceType1) + assert.False(t, result, "Int should NOT be a subtype of interface") + }) + }) + + t.Run("ParameterizedType", func(t *testing.T) { + t.Parallel() + + // This test uses sema.CapabilityType which is a sema.ParameterizedType + + t.Run("capability with matching borrow type", func(t *testing.T) { + capability1 := &sema.CapabilityType{ + BorrowType: sema.IntType, + } + capability2 := &sema.CapabilityType{ + BorrowType: sema.NumberType, + } + + result := checkSubTypeFunctions(t, capability1, capability2) + assert.True(t, result, "Capability should be a subtype of Capability") + }) + + t.Run("capability with non-matching borrow type", func(t *testing.T) { + capability1 := &sema.CapabilityType{ + BorrowType: sema.IntType, + } + capability2 := &sema.CapabilityType{ + BorrowType: sema.StringType, + } + + result := checkSubTypeFunctions(t, capability1, capability2) + assert.False(t, result, "Capability should NOT be a subtype of Capability") + }) + + t.Run("non-capability is not subtype of capability", func(t *testing.T) { + capability := &sema.CapabilityType{ + BorrowType: sema.IntType, + } + + result := checkSubTypeFunctions(t, sema.IntType, capability) + // This may pass or fail depending on sema.IsParameterizedSubType fallback + // The function checks if sema.IntType's base type is a subtype of capability + assert.False(t, result, "Int should NOT be a subtype of Capability") + }) + + t.Run("parameterized type with nil sema.BaseType", func(t *testing.T) { + // Capability with nil sema.BorrowType has nil sema.BaseType + nilCapability := &sema.CapabilityType{ + BorrowType: nil, + } + + // Int is NOT <: Capability + result := checkSubTypeFunctions(t, sema.IntType, nilCapability) + assert.False(t, result, "Int should NOT be a subtype of Capability with nil sema.BorrowType") + }) + + t.Run("parameterized type with base types not matching", func(t *testing.T) { + // This tests the case where base types don't match + capability := &sema.CapabilityType{ + BorrowType: sema.IntType, + } + inclusiveRange := &sema.InclusiveRangeType{ + MemberType: sema.IntType, + } + + // The base types need to be subtypes first + result := checkSubTypeFunctions(t, capability, inclusiveRange) + assert.False(t, result, "base types must match for parameterized subtypes") + }) + }) + + t.Run("unhandled type", func(t *testing.T) { + // sema.TransactionType doesn't match any special case in the switch statement + // so this falls back to the default-case. + result := checkSubTypeFunctions(t, sema.IntType, &sema.TransactionType{ + Location: common.TransactionLocation{}, + }) + assert.False(t, result, "Int should NOT be a subtype of sema.TransactionType") + }) +} diff --git a/sema/subtype_check.gen.go b/sema/subtype_check.gen.go index 5e3986c2fb..852f2dd0cd 100644 --- a/sema/subtype_check.gen.go +++ b/sema/subtype_check.gen.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/sema/subtype_check_test.go b/sema/subtype_check_test.go deleted file mode 100644 index 9168ee2423..0000000000 --- a/sema/subtype_check_test.go +++ /dev/null @@ -1,1882 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Flow Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sema - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/onflow/cadence/common" -) - -// checkSubTypeFunctions calls both checkSubTypeWithoutEquality and checkSubTypeWithoutEquality_gen -// and asserts they produce the same result -func checkSubTypeFunctions(t *testing.T, subType Type, superType Type) bool { - result := checkSubTypeWithoutEquality(subType, superType) - generatedResult := checkSubTypeWithoutEquality_gen(subType, superType) - - assert.Equal( - t, - result, - generatedResult, - "generated function produced different results for"+ - " subType=%s, superType=%s: manual=%s, generated=%s", - subType, - superType, - result, - generatedResult, - ) - - return result -} - -// TestCheckSubTypeWithoutEquality tests all paths of checkSubTypeWithoutEquality function -func TestCheckSubTypeWithoutEquality(t *testing.T) { - t.Parallel() - - t.Run("NeverType", func(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - superType Type - }{ - {"Never <: Any", AnyType}, - {"Never <: AnyStruct", AnyStructType}, - {"Never <: AnyResource", AnyResourceType}, - {"Never <: Int", IntType}, - {"Never <: String", StringType}, - {"Never <: Bool", BoolType}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, NeverType, tt.superType) - assert.True(t, result, "NeverType should be a subtype of %v", tt.superType) - }) - } - }) - - t.Run("AnyType", func(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - subType Type - }{ - {"Int <: Any", IntType}, - {"String <: Any", StringType}, - {"Bool <: Any", BoolType}, - {"AnyStruct <: Any", AnyStructType}, - {"AnyResource <: Any", AnyResourceType}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, tt.subType, AnyType) - assert.True(t, result, "%v should be a subtype of AnyType", tt.subType) - }) - } - }) - - t.Run("AnyStructType", func(t *testing.T) { - t.Parallel() - - t.Run("struct types are subtypes of AnyStruct", func(t *testing.T) { - tests := []struct { - name string - subType Type - }{ - {"Int <: AnyStruct", IntType}, - {"String <: AnyStruct", StringType}, - {"Bool <: AnyStruct", BoolType}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, tt.subType, AnyStructType) - assert.True(t, result, "%v should be a subtype of AnyStructType", tt.subType) - }) - } - }) - - t.Run("resource types are NOT subtypes of AnyStruct", func(t *testing.T) { - result := checkSubTypeFunctions(t, AnyResourceType, AnyStructType) - assert.False(t, result, "AnyResource should NOT be a subtype of AnyStruct") - }) - - t.Run("AnyType is NOT a subtype of AnyStruct", func(t *testing.T) { - result := checkSubTypeFunctions(t, AnyType, AnyStructType) - assert.False(t, result, "AnyType should NOT be a subtype of AnyStruct") - }) - }) - - t.Run("AnyResourceType", func(t *testing.T) { - t.Parallel() - - t.Run("resource types are subtypes of AnyResource", func(t *testing.T) { - result := checkSubTypeFunctions(t, AnyResourceType, AnyResourceType) - assert.True(t, result, "AnyResource should be a subtype of AnyResource") - }) - - t.Run("struct types are NOT subtypes of AnyResource", func(t *testing.T) { - tests := []Type{ - IntType, - StringType, - BoolType, - AnyStructType, - } - - for _, subType := range tests { - result := checkSubTypeFunctions(t, subType, AnyResourceType) - assert.False(t, result, "%v should NOT be a subtype of AnyResource", subType) - } - }) - }) - - t.Run("AttachmentTypes", func(t *testing.T) { - t.Parallel() - - t.Run("AnyResourceAttachment", func(t *testing.T) { - // Note: Testing with real attachment types would require more setup - // These tests verify the basic structure - t.Run("non-resource is not subtype", func(t *testing.T) { - result := checkSubTypeFunctions(t, IntType, AnyResourceAttachmentType) - assert.False(t, result) - }) - - t.Run("struct is not subtype", func(t *testing.T) { - result := checkSubTypeFunctions(t, StringType, AnyResourceAttachmentType) - assert.False(t, result) - }) - - t.Run("AnyStruct is not subtype", func(t *testing.T) { - result := checkSubTypeFunctions(t, AnyStructType, AnyResourceAttachmentType) - assert.False(t, result) - }) - }) - - t.Run("AnyStructAttachment", func(t *testing.T) { - t.Run("resource is not subtype", func(t *testing.T) { - result := checkSubTypeFunctions(t, AnyResourceType, AnyStructAttachmentType) - assert.False(t, result) - }) - - t.Run("non-attachment struct is not subtype", func(t *testing.T) { - result := checkSubTypeFunctions(t, IntType, AnyStructAttachmentType) - assert.False(t, result) - }) - - t.Run("AnyResource is not subtype", func(t *testing.T) { - result := checkSubTypeFunctions(t, AnyResourceType, AnyStructAttachmentType) - assert.False(t, result) - }) - }) - }) - - t.Run("HashableStructType", func(t *testing.T) { - t.Parallel() - - t.Run("hashable types are subtypes", func(t *testing.T) { - tests := []Type{ - IntType, - StringType, - BoolType, - TheAddressType, - } - - for _, subType := range tests { - result := checkSubTypeFunctions(t, subType, HashableStructType) - assert.True(t, result, "%v should be a subtype of HashableStruct", subType) - } - }) - }) - - t.Run("PathTypes", func(t *testing.T) { - t.Parallel() - - t.Run("PathType", func(t *testing.T) { - t.Run("StoragePath <: Path", func(t *testing.T) { - result := checkSubTypeFunctions(t, StoragePathType, PathType) - assert.True(t, result) - }) - - t.Run("PrivatePath <: Path", func(t *testing.T) { - result := checkSubTypeFunctions(t, PrivatePathType, PathType) - assert.True(t, result) - }) - - t.Run("PublicPath <: Path", func(t *testing.T) { - result := checkSubTypeFunctions(t, PublicPathType, PathType) - assert.True(t, result) - }) - - t.Run("Int is NOT <: Path", func(t *testing.T) { - result := checkSubTypeFunctions(t, IntType, PathType) - assert.False(t, result) - }) - }) - - t.Run("CapabilityPathType", func(t *testing.T) { - t.Run("PrivatePath <: CapabilityPath", func(t *testing.T) { - result := checkSubTypeFunctions(t, PrivatePathType, CapabilityPathType) - assert.True(t, result) - }) - - t.Run("PublicPath <: CapabilityPath", func(t *testing.T) { - result := checkSubTypeFunctions(t, PublicPathType, CapabilityPathType) - assert.True(t, result) - }) - - t.Run("StoragePath is NOT <: CapabilityPath", func(t *testing.T) { - result := checkSubTypeFunctions(t, StoragePathType, CapabilityPathType) - assert.False(t, result) - }) - }) - }) - - t.Run("StorableType", func(t *testing.T) { - t.Parallel() - - t.Run("storable types are subtypes", func(t *testing.T) { - tests := []Type{ - IntType, - StringType, - BoolType, - TheAddressType, - } - - for _, subType := range tests { - result := checkSubTypeFunctions(t, subType, StorableType) - assert.True(t, result, "%v should be a subtype of Storable", subType) - } - }) - }) - - t.Run("NumberTypes", func(t *testing.T) { - t.Parallel() - - t.Run("NumberType", func(t *testing.T) { - tests := []struct { - name string - subType Type - expected bool - }{ - {"NumberType <: Number", NumberType, true}, - {"SignedNumberType <: Number", SignedNumberType, true}, - {"Int <: Number", IntType, true}, - {"Int8 <: Number", Int8Type, true}, - {"UInt <: Number", UIntType, true}, - {"Fix64 <: Number", Fix64Type, true}, - {"UFix64 <: Number", UFix64Type, true}, - {"String is NOT <: Number", StringType, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, tt.subType, NumberType) - assert.Equal(t, tt.expected, result) - }) - } - }) - - t.Run("SignedNumberType", func(t *testing.T) { - tests := []struct { - name string - subType Type - expected bool - }{ - {"SignedNumberType <: SignedNumber", SignedNumberType, true}, - {"Int <: SignedNumber", IntType, true}, - {"Int8 <: SignedNumber", Int8Type, true}, - {"Fix64 <: SignedNumber", Fix64Type, true}, - {"UInt is NOT <: SignedNumber", UIntType, false}, - {"UFix64 is NOT <: SignedNumber", UFix64Type, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, tt.subType, SignedNumberType) - assert.Equal(t, tt.expected, result) - }) - } - }) - - t.Run("IntegerType", func(t *testing.T) { - tests := []struct { - name string - subType Type - expected bool - }{ - {"IntegerType <: Integer", IntegerType, true}, - {"SignedIntegerType <: Integer", SignedIntegerType, true}, - {"FixedSizeUnsignedIntegerType <: Integer", FixedSizeUnsignedIntegerType, true}, - {"UIntType <: Integer", UIntType, true}, - {"Int <: Integer", IntType, true}, - {"UInt8 <: Integer", UInt8Type, true}, - {"Fix64 is NOT <: Integer", Fix64Type, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, tt.subType, IntegerType) - assert.Equal(t, tt.expected, result) - }) - } - }) - - t.Run("SignedIntegerType", func(t *testing.T) { - tests := []struct { - name string - subType Type - expected bool - }{ - {"SignedIntegerType <: SignedInteger", SignedIntegerType, true}, - {"Int <: SignedInteger", IntType, true}, - {"Int8 <: SignedInteger", Int8Type, true}, - {"Int16 <: SignedInteger", Int16Type, true}, - {"Int32 <: SignedInteger", Int32Type, true}, - {"Int64 <: SignedInteger", Int64Type, true}, - {"Int128 <: SignedInteger", Int128Type, true}, - {"Int256 <: SignedInteger", Int256Type, true}, - {"UInt is NOT <: SignedInteger", UIntType, false}, - {"UInt8 is NOT <: SignedInteger", UInt8Type, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, tt.subType, SignedIntegerType) - assert.Equal(t, tt.expected, result) - }) - } - }) - - t.Run("FixedSizeUnsignedIntegerType", func(t *testing.T) { - tests := []struct { - name string - subType Type - expected bool - }{ - {"UInt8 <: FixedSizeUnsignedInteger", UInt8Type, true}, - {"UInt16 <: FixedSizeUnsignedInteger", UInt16Type, true}, - {"UInt32 <: FixedSizeUnsignedInteger", UInt32Type, true}, - {"UInt64 <: FixedSizeUnsignedInteger", UInt64Type, true}, - {"UInt128 <: FixedSizeUnsignedInteger", UInt128Type, true}, - {"UInt256 <: FixedSizeUnsignedInteger", UInt256Type, true}, - {"Word8 <: FixedSizeUnsignedInteger", Word8Type, true}, - {"Word16 <: FixedSizeUnsignedInteger", Word16Type, true}, - {"Word32 <: FixedSizeUnsignedInteger", Word32Type, true}, - {"Word64 <: FixedSizeUnsignedInteger", Word64Type, true}, - {"Word128 <: FixedSizeUnsignedInteger", Word128Type, true}, - {"Word256 <: FixedSizeUnsignedInteger", Word256Type, true}, - {"UInt is NOT <: FixedSizeUnsignedInteger", UIntType, false}, - {"Int is NOT <: FixedSizeUnsignedInteger", IntType, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, tt.subType, FixedSizeUnsignedIntegerType) - assert.Equal(t, tt.expected, result) - }) - } - }) - - t.Run("FixedPointType", func(t *testing.T) { - tests := []struct { - name string - subType Type - expected bool - }{ - {"FixedPointType <: FixedPoint", FixedPointType, true}, - {"SignedFixedPointType <: FixedPoint", SignedFixedPointType, true}, - {"UFix64 <: FixedPoint", UFix64Type, true}, - {"UFix128 <: FixedPoint", UFix128Type, true}, - {"Fix64 <: FixedPoint", Fix64Type, true}, - {"Fix128 <: FixedPoint", Fix128Type, true}, - {"Int is NOT <: FixedPoint", IntType, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, tt.subType, FixedPointType) - assert.Equal(t, tt.expected, result) - }) - } - }) - - t.Run("SignedFixedPointType", func(t *testing.T) { - tests := []struct { - name string - subType Type - expected bool - }{ - {"SignedFixedPointType <: SignedFixedPoint", SignedFixedPointType, true}, - {"Fix64 <: SignedFixedPoint", Fix64Type, true}, - {"Fix128 <: SignedFixedPoint", Fix128Type, true}, - {"UFix64 is NOT <: SignedFixedPoint", UFix64Type, false}, - {"UFix128 is NOT <: SignedFixedPoint", UFix128Type, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := checkSubTypeFunctions(t, tt.subType, SignedFixedPointType) - assert.Equal(t, tt.expected, result) - }) - } - }) - }) - - t.Run("OptionalType", func(t *testing.T) { - t.Parallel() - - t.Run("T <: T?", func(t *testing.T) { - optionalInt := &OptionalType{Type: IntType} - result := checkSubTypeFunctions(t, IntType, optionalInt) - assert.True(t, result, "Int should be a subtype of Int?") - }) - - t.Run("T? <: U? when T <: U", func(t *testing.T) { - optionalNumber := &OptionalType{Type: NumberType} - optionalInt := &OptionalType{Type: IntType} - result := checkSubTypeFunctions(t, optionalInt, optionalNumber) - assert.True(t, result, "Int? should be a subtype of Number?") - }) - - t.Run("T? is NOT <: U? when T is NOT <: U", func(t *testing.T) { - optionalInt := &OptionalType{Type: IntType} - optionalString := &OptionalType{Type: StringType} - result := checkSubTypeFunctions(t, optionalInt, optionalString) - assert.False(t, result, "Int? should NOT be a subtype of String?") - }) - - t.Run("nested optionals", func(t *testing.T) { - // Int <: Int?? - doubleOptionalInt := &OptionalType{ - Type: &OptionalType{Type: IntType}, - } - result := checkSubTypeFunctions(t, IntType, doubleOptionalInt) - assert.True(t, result, "Int should be a subtype of Int??") - }) - }) - - t.Run("DictionaryType", func(t *testing.T) { - t.Parallel() - - t.Run("covariant in key and value types", func(t *testing.T) { - dict1 := &DictionaryType{ - KeyType: IntType, - ValueType: IntType, - } - dict2 := &DictionaryType{ - KeyType: NumberType, - ValueType: NumberType, - } - result := checkSubTypeFunctions(t, dict1, dict2) - assert.True(t, result, "{Int: Int} should be a subtype of {Number: Number}") - }) - - t.Run("not subtype when key types don't match", func(t *testing.T) { - dict1 := &DictionaryType{ - KeyType: IntType, - ValueType: IntType, - } - dict2 := &DictionaryType{ - KeyType: StringType, - ValueType: IntType, - } - result := checkSubTypeFunctions(t, dict1, dict2) - assert.False(t, result, "{Int: Int} should NOT be a subtype of {String: Int}") - }) - - t.Run("not subtype when value types don't match", func(t *testing.T) { - dict1 := &DictionaryType{ - KeyType: IntType, - ValueType: IntType, - } - dict2 := &DictionaryType{ - KeyType: IntType, - ValueType: StringType, - } - result := checkSubTypeFunctions(t, dict1, dict2) - assert.False(t, result, "{Int: Int} should NOT be a subtype of {Int: String}") - }) - - t.Run("non-dictionary is not subtype", func(t *testing.T) { - dict := &DictionaryType{ - KeyType: IntType, - ValueType: StringType, - } - result := checkSubTypeFunctions(t, IntType, dict) - assert.False(t, result, "Int should NOT be a subtype of {Int: String}") - }) - - t.Run("dictionary with optional values", func(t *testing.T) { - dict1 := &DictionaryType{ - KeyType: StringType, - ValueType: IntType, - } - dict2 := &DictionaryType{ - KeyType: StringType, - ValueType: &OptionalType{Type: IntType}, - } - result := checkSubTypeFunctions(t, dict1, dict2) - assert.True(t, result, "{String: Int} should be a subtype of {String: Int?}") - }) - }) - - t.Run("VariableSizedType", func(t *testing.T) { - t.Parallel() - - t.Run("covariant in element type", func(t *testing.T) { - arr1 := &VariableSizedType{Type: IntType} - arr2 := &VariableSizedType{Type: NumberType} - result := checkSubTypeFunctions(t, arr1, arr2) - assert.True(t, result, "[Int] should be a subtype of [Number]") - }) - - t.Run("not subtype when element types don't match", func(t *testing.T) { - arr1 := &VariableSizedType{Type: IntType} - arr2 := &VariableSizedType{Type: StringType} - result := checkSubTypeFunctions(t, arr1, arr2) - assert.False(t, result, "[Int] should NOT be a subtype of [String]") - }) - - t.Run("non-array is not subtype", func(t *testing.T) { - arr := &VariableSizedType{Type: IntType} - result := checkSubTypeFunctions(t, IntType, arr) - assert.False(t, result, "Int should NOT be a subtype of [Int]") - }) - - t.Run("nested arrays", func(t *testing.T) { - // [[Int]] <: [[Number]] - arrInt := &VariableSizedType{ - Type: &VariableSizedType{Type: IntType}, - } - arrNumber := &VariableSizedType{ - Type: &VariableSizedType{Type: NumberType}, - } - result := checkSubTypeFunctions(t, arrInt, arrNumber) - assert.True(t, result, "[[Int]] should be a subtype of [[Number]]") - }) - }) - - t.Run("ConstantSizedType", func(t *testing.T) { - t.Parallel() - - t.Run("covariant in element type with same size", func(t *testing.T) { - arr1 := &ConstantSizedType{Type: IntType, Size: 5} - arr2 := &ConstantSizedType{Type: NumberType, Size: 5} - result := checkSubTypeFunctions(t, arr1, arr2) - assert.True(t, result, "[Int; 5] should be a subtype of [Number; 5]") - }) - - t.Run("not subtype when sizes differ", func(t *testing.T) { - arr1 := &ConstantSizedType{Type: IntType, Size: 5} - arr2 := &ConstantSizedType{Type: IntType, Size: 10} - result := checkSubTypeFunctions(t, arr1, arr2) - assert.False(t, result, "[Int; 5] should NOT be a subtype of [Int; 10]") - }) - - t.Run("not subtype when element types don't match", func(t *testing.T) { - arr1 := &ConstantSizedType{Type: IntType, Size: 5} - arr2 := &ConstantSizedType{Type: StringType, Size: 5} - result := checkSubTypeFunctions(t, arr1, arr2) - assert.False(t, result, "[Int; 5] should NOT be a subtype of [String; 5]") - }) - - t.Run("non-array is not subtype", func(t *testing.T) { - arr := &ConstantSizedType{Type: IntType, Size: 5} - result := checkSubTypeFunctions(t, IntType, arr) - assert.False(t, result, "Int should NOT be a subtype of [Int; 5]") - }) - }) - - t.Run("ReferenceType", func(t *testing.T) { - t.Parallel() - - t.Run("covariant in referenced type with compatible authorization", func(t *testing.T) { - ref1 := &ReferenceType{ - Type: IntType, - Authorization: UnauthorizedAccess, - } - ref2 := &ReferenceType{ - Type: NumberType, - Authorization: UnauthorizedAccess, - } - result := checkSubTypeFunctions(t, ref1, ref2) - assert.True(t, result, "&Int should be a subtype of &Number") - }) - - t.Run("not subtype when authorization doesn't permit", func(t *testing.T) { - entitlement := NewEntitlementType(nil, common.NewStringLocation(nil, "test"), "E") - auth := NewEntitlementSetAccess([]*EntitlementType{entitlement}, Disjunction) - - ref1 := &ReferenceType{ - Type: IntType, - Authorization: UnauthorizedAccess, - } - ref2 := &ReferenceType{ - Type: IntType, - Authorization: auth, - } - result := checkSubTypeFunctions(t, ref1, ref2) - assert.False(t, result, "unauthorized reference should NOT be a subtype of authorized reference") - }) - - t.Run("not subtype when referenced types don't match", func(t *testing.T) { - ref1 := &ReferenceType{ - Type: IntType, - Authorization: UnauthorizedAccess, - } - ref2 := &ReferenceType{ - Type: StringType, - Authorization: UnauthorizedAccess, - } - result := checkSubTypeFunctions(t, ref1, ref2) - assert.False(t, result, "&Int should NOT be a subtype of &String") - }) - - t.Run("non-reference is not subtype", func(t *testing.T) { - ref := &ReferenceType{ - Type: IntType, - Authorization: UnauthorizedAccess, - } - result := checkSubTypeFunctions(t, IntType, ref) - assert.False(t, result, "Int should NOT be a subtype of &Int") - }) - - t.Run("reference to resource type", func(t *testing.T) { - // &AnyResource <: &AnyResource - ref1 := &ReferenceType{ - Type: AnyResourceType, - Authorization: UnauthorizedAccess, - } - ref2 := &ReferenceType{ - Type: AnyResourceType, - Authorization: UnauthorizedAccess, - } - result := checkSubTypeFunctions(t, ref1, ref2) - assert.True(t, result, "&AnyResource should be a subtype of &AnyResource") - }) - - t.Run("reference to optional type", func(t *testing.T) { - // &Int <: &Int? - refToInt := &ReferenceType{ - Type: IntType, - Authorization: UnauthorizedAccess, - } - refToOptInt := &ReferenceType{ - Type: &OptionalType{Type: IntType}, - Authorization: UnauthorizedAccess, - } - result := checkSubTypeFunctions(t, refToInt, refToOptInt) - assert.True(t, result, "&Int should be a subtype of &Int?") - }) - - t.Run("reference with same authorization different types", func(t *testing.T) { - // &String is NOT <: &Int even with same auth - ref1 := &ReferenceType{ - Type: StringType, - Authorization: UnauthorizedAccess, - } - ref2 := &ReferenceType{ - Type: IntType, - Authorization: UnauthorizedAccess, - } - result := checkSubTypeFunctions(t, ref1, ref2) - assert.False(t, result, "&String should NOT be a subtype of &Int") - }) - }) - - t.Run("FunctionType", func(t *testing.T) { - t.Parallel() - - t.Run("view function is subtype of impure function", func(t *testing.T) { - viewFunc := &FunctionType{ - Purity: FunctionPurityView, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - impureFunc := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - result := checkSubTypeFunctions(t, viewFunc, impureFunc) - assert.True(t, result, "view function should be a subtype of impure function") - }) - - t.Run("impure function is NOT subtype of view function", func(t *testing.T) { - viewFunc := &FunctionType{ - Purity: FunctionPurityView, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - impureFunc := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - result := checkSubTypeFunctions(t, impureFunc, viewFunc) - assert.False(t, result, "impure function should NOT be a subtype of view function") - }) - - t.Run("contravariant in parameter types", func(t *testing.T) { - // fun(Number): Int <: fun(Int): Int - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(NumberType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - result := checkSubTypeFunctions(t, func1, func2) - assert.True(t, result, "fun(Number): Int should be a subtype of fun(Int): Int") - }) - - t.Run("covariant in return type", func(t *testing.T) { - // fun(Int): Int <: fun(Int): Number - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(NumberType), - } - result := checkSubTypeFunctions(t, func1, func2) - assert.True(t, result, "fun(Int): Int should be a subtype of fun(Int): Number") - }) - - t.Run("not subtype when parameter contravariance fails", func(t *testing.T) { - // fun(Int): Int is NOT <: fun(Number): Int - // Because Number is NOT <: Int (contravariance requirement fails) - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(NumberType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - result := checkSubTypeFunctions(t, func1, func2) - assert.False(t, result, "fun(Int): Int should NOT be subtype of fun(Number): Int (contravariance fails)") - }) - - t.Run("not subtype when parameter arity differs", func(t *testing.T) { - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - result := checkSubTypeFunctions(t, func1, func2) - assert.False(t, result, "functions with different arities should NOT be subtypes") - }) - - t.Run("not subtype when constructor flags differ", func(t *testing.T) { - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - IsConstructor: true, - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - IsConstructor: false, - } - result := checkSubTypeFunctions(t, func1, func2) - assert.False(t, result, "constructor and non-constructor functions should NOT be subtypes") - }) - - t.Run("non-function is not subtype", func(t *testing.T) { - fn := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{}, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - result := checkSubTypeFunctions(t, IntType, fn) - assert.False(t, result, "Int should NOT be a subtype of function") - }) - - t.Run("function with Void return type", func(t *testing.T) { - // fun(Int): Void <: fun(Int): Void - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: VoidTypeAnnotation, - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: VoidTypeAnnotation, - } - result := checkSubTypeFunctions(t, func1, func2) - assert.True(t, result, "functions with Void return should be subtypes") - }) - - t.Run("function with different return types", func(t *testing.T) { - // fun(Int): String is NOT <: fun(Int): Int - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(StringType), - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - } - result := checkSubTypeFunctions(t, func1, func2) - assert.False(t, result, "function with String return should NOT be subtype of Int return") - }) - - t.Run("function with different arity", func(t *testing.T) { - // fun() is NOT <: fun(Int) - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{}, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - Arity: &Arity{Min: 0, Max: 0}, - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(IntType)}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - Arity: &Arity{Min: 1, Max: 1}, - } - result := checkSubTypeFunctions(t, func1, func2) - assert.False(t, result, "functions with different arity should NOT be subtypes") - }) - - t.Run("function with type parameters not equal", func(t *testing.T) { - // fun() is NOT <: fun() - typeParam1 := &TypeParameter{ - Name: "T", - TypeBound: IntType, - } - typeParam2 := &TypeParameter{ - Name: "T", - TypeBound: StringType, - } - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{}, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - TypeParameters: []*TypeParameter{typeParam1}, - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{}, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - TypeParameters: []*TypeParameter{typeParam2}, - } - result := checkSubTypeFunctions(t, func1, func2) - assert.False(t, result, "functions with different type parameter bounds should NOT be subtypes") - }) - - t.Run("function with different type parameter count", func(t *testing.T) { - // fun() is NOT <: fun() - typeParam := &TypeParameter{ - Name: "T", - TypeBound: IntType, - } - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{}, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - TypeParameters: []*TypeParameter{typeParam}, - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{}, - ReturnTypeAnnotation: NewTypeAnnotation(IntType), - TypeParameters: []*TypeParameter{typeParam, typeParam}, - } - result := checkSubTypeFunctions(t, func1, func2) - assert.False(t, result, "functions with different type parameter count should NOT be subtypes") - }) - - t.Run("function returning array", func(t *testing.T) { - // fun(): [Int] <: fun(): [Number] - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{}, - ReturnTypeAnnotation: NewTypeAnnotation( - &VariableSizedType{Type: IntType}, - ), - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{}, - ReturnTypeAnnotation: NewTypeAnnotation( - &VariableSizedType{Type: NumberType}, - ), - } - result := checkSubTypeFunctions(t, func1, func2) - assert.True(t, result, "fun(): [Int] should be subtype of fun(): [Number]") - }) - - t.Run("function with array parameter", func(t *testing.T) { - // fun([Number]): Void <: fun([Int]): Void (contravariance) - func1 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(&VariableSizedType{Type: NumberType})}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(VoidType), - } - func2 := &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - {TypeAnnotation: NewTypeAnnotation(&VariableSizedType{Type: IntType})}, - }, - ReturnTypeAnnotation: NewTypeAnnotation(VoidType), - } - result := checkSubTypeFunctions(t, func1, func2) - assert.True(t, result, "fun([Number]): Void should be subtype of fun([Int]): Void") - }) - }) - - t.Run("IntersectionType", func(t *testing.T) { - t.Parallel() - - // Create test interfaces for intersection types - location := common.NewStringLocation(nil, "test") - - interfaceType1 := &InterfaceType{ - Location: location, - Identifier: "I1", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - interfaceType2 := &InterfaceType{ - Location: location, - Identifier: "I2", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - t.Run("AnyResource intersection with nil subtype", func(t *testing.T) { - result := checkSubTypeFunctions(t, - AnyResourceType, - &IntersectionType{ - LegacyType: AnyResourceType, - Types: []*InterfaceType{interfaceType1}, - }, - ) - assert.False(t, result, "AnyResource should NOT be a subtype of AnyResource{I}") - }) - - t.Run("AnyStruct intersection with nil subtype", func(t *testing.T) { - result := checkSubTypeFunctions(t, - AnyStructType, - &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{interfaceType1}, - }, - ) - assert.False(t, result, "AnyStruct should NOT be a subtype of AnyStruct{I}") - }) - - t.Run("Any intersection with nil subtype", func(t *testing.T) { - result := checkSubTypeFunctions(t, - AnyType, - &IntersectionType{ - LegacyType: AnyType, - Types: []*InterfaceType{interfaceType1}, - }, - ) - assert.False(t, result, "Any should NOT be a subtype of Any{I}") - }) - - // Tests for IntersectionType subtype with nil LegacyType - t.Run("intersection with nil legacy type as subtype", func(t *testing.T) { - // {I1, I2} <: {I1} when I1 is subset of {I1, I2} - subType := &IntersectionType{ - Types: []*InterfaceType{interfaceType1, interfaceType2}, - } - superType := &IntersectionType{ - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.True(t, result, "{I1, I2} should be a subtype of {I1}") - }) - - t.Run("intersection with nil legacy type not subtype when not subset", func(t *testing.T) { - // {I1} is NOT <: {I2} - subType := &IntersectionType{ - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "{I1} should NOT be a subtype of {I2}") - }) - - // Tests for IntersectionType subtype with AnyResource LegacyType - t.Run("AnyResource intersection subtype with matching interfaces", func(t *testing.T) { - interfaceType2 := &InterfaceType{ - Location: location, - Identifier: "I2", - CompositeKind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - } - - // AnyResource{I1, I2} <: AnyResource{I1} - subType := &IntersectionType{ - LegacyType: AnyResourceType, - Types: []*InterfaceType{interfaceType1, interfaceType2}, - } - superType := &IntersectionType{ - LegacyType: AnyResourceType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.True(t, result, "AnyResource{I1, I2} should be a subtype of AnyResource{I1}") - }) - - t.Run("AnyResource intersection not subtype of AnyStruct intersection", func(t *testing.T) { - subType := &IntersectionType{ - LegacyType: AnyResourceType, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "AnyResource{I} should NOT be a subtype of AnyStruct{I}") - }) - - // Tests for IntersectionType subtype with CompositeType LegacyType - t.Run("composite intersection subtype when interfaces match", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "R", - Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - } - compositeType.ExplicitInterfaceConformances = []*InterfaceType{interfaceType1} - - // R{I1} <: AnyResource{} when R conforms to I1 - subType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: AnyResourceType, - Types: []*InterfaceType{}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.True(t, result, "R{I1} should be a subtype of AnyResource{}") - }) - - t.Run("composite intersection not subtype when composite doesn't conform", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "R", - Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - } - // No conformance set - - interfaceType2 := &InterfaceType{ - Location: location, - Identifier: "I2", - CompositeKind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - } - - // R{I2} is NOT <: AnyResource{I1} when R doesn't conform to I1 - subType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType2}, - } - superType := &IntersectionType{ - LegacyType: AnyResourceType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "R{I2} should NOT be a subtype of AnyResource{I1} when R doesn't conform") - }) - - // Tests for ConformingType (CompositeType) as subtype of IntersectionType - t.Run("composite type subtype of intersection when conforming", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - compositeType.ExplicitInterfaceConformances = []*InterfaceType{interfaceType1} - - // S <: AnyStruct{I1} when S conforms to I1 - superType := &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, compositeType, superType) - assert.True(t, result, "composite should be a subtype of intersection when conforming") - }) - - t.Run("composite type not subtype of intersection when not conforming", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - // No conformance - - interfaceType2 := &InterfaceType{ - Location: location, - Identifier: "I2", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // S is NOT <: AnyStruct{I2} when S doesn't conform to I2 - superType := &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, compositeType, superType) - assert.False(t, result, "composite should NOT be a subtype of intersection when not conforming") - }) - - t.Run("composite type not subtype when wrong base type", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "R", - Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - } - compositeType.ExplicitInterfaceConformances = []*InterfaceType{interfaceType1} - - // Resource R is NOT <: AnyStruct{I1} even if conforming - superType := &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, compositeType, superType) - assert.False(t, result, "resource should NOT be a subtype of struct intersection") - }) - - // Tests for IntersectionType supertype with non-Any* legacy type - t.Run("intersection with composite legacy type", func(t *testing.T) { - compositeType1 := &CompositeType{ - Location: location, - Identifier: "S1", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // S1{I1} <: S1{I2} when S1 == S1 (owner may restrict/unrestrict) - subType := &IntersectionType{ - LegacyType: compositeType1, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: compositeType1, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.True(t, result, "S1{I1} should be a subtype of S1{I2} when same composite") - }) - - t.Run("intersection with different composite legacy types", func(t *testing.T) { - compositeType1 := &CompositeType{ - Location: location, - Identifier: "S1", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - compositeType2 := &CompositeType{ - Location: location, - Identifier: "S2", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // S1{I1} is NOT <: S2{I1} when S1 != S2 - subType := &IntersectionType{ - LegacyType: compositeType1, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: compositeType2, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "S1{I1} should NOT be a subtype of S2{I1} when different composites") - }) - - t.Run("intersection with interface legacy not subtype of composite intersection", func(t *testing.T) { - interfaceType3 := &InterfaceType{ - Location: location, - Identifier: "I3", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // I3{I1} is NOT <: S{I2} (interface legacy type vs composite legacy type) - subType := &IntersectionType{ - LegacyType: interfaceType3, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "I3{I1} should NOT be a subtype of S{I2}") - }) - - t.Run("composite type subtype of composite intersection", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // S <: S{I1} (owner may freely restrict) - superType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, compositeType, superType) - assert.True(t, result, "composite should be a subtype of its own intersection type") - }) - - t.Run("intersection nil legacy type not subtype of composite intersection", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // {I1} is NOT <: S{I1} statically - subType := &IntersectionType{ - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "{I1} should NOT be statically a subtype of S{I1}") - }) - - t.Run("intersection Any* legacy type not subtype of composite intersection", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // AnyStruct{I1} is NOT <: S{I1} statically - subType := &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "AnyStruct{I1} should NOT be statically a subtype of S{I1}") - }) - - t.Run("AnyResourceType not subtype of composite intersection", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "R", - Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - } - - // AnyResource is NOT <: R{I1} statically - superType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, AnyResourceType, superType) - assert.False(t, result, "AnyResource should NOT be statically a subtype of R{I1}") - }) - - t.Run("AnyStructType not subtype of composite intersection", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // AnyStruct is NOT <: S{I1} statically - superType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, AnyStructType, superType) - assert.False(t, result, "AnyStruct should NOT be statically a subtype of S{I1}") - }) - - t.Run("AnyType not subtype of composite intersection", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // Any is NOT <: S{I1} statically - superType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, AnyType, superType) - assert.False(t, result, "Any should NOT be statically a subtype of S{I1}") - }) - - t.Run("optional type not subtype of composite intersection", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // Int? is NOT <: S{I1} - optionalType := &OptionalType{Type: IntType} - superType := &IntersectionType{ - LegacyType: compositeType, - Types: []*InterfaceType{interfaceType1}, - } - result := checkSubTypeFunctions(t, optionalType, superType) - assert.False(t, result, "Int? should NOT be subtype of S{I1}") - }) - - t.Run("struct composite intersection not subtype of AnyResource intersection", func(t *testing.T) { - structCompositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // S{I1} is NOT <: AnyResource{I2} because S is not a resource - subType := &IntersectionType{ - LegacyType: structCompositeType, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: AnyResourceType, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "S{I1} should NOT be subtype of AnyResource{I2} (struct vs resource)") - }) - - t.Run("resource composite intersection is subtype of AnyResource intersection with conformance", func(t *testing.T) { - resourceCompositeType := &CompositeType{ - Location: location, - Identifier: "R", - Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - ExplicitInterfaceConformances: []*InterfaceType{interfaceType1, interfaceType2}, - } - - // R{I1} <: AnyResource{I2} if R is a resource and R conforms to I2 - subType := &IntersectionType{ - LegacyType: resourceCompositeType, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: AnyResourceType, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.True(t, result, "R{I1} should be subtype of AnyResource{I2} when R conforms to I2") - }) - - t.Run("resource composite intersection not subtype of AnyResource without conformance", func(t *testing.T) { - resourceCompositeType := &CompositeType{ - Location: location, - Identifier: "R", - Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - ExplicitInterfaceConformances: []*InterfaceType{interfaceType1}, - } - - // R{I1} is NOT <: AnyResource{I2} if R doesn't conform to I2 - subType := &IntersectionType{ - LegacyType: resourceCompositeType, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: AnyResourceType, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "R{I1} should NOT be subtype of AnyResource{I2} when R doesn't conform to I2") - }) - - t.Run("struct composite intersection is subtype of AnyStruct intersection with conformance", func(t *testing.T) { - structCompositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - ExplicitInterfaceConformances: []*InterfaceType{interfaceType1, interfaceType2}, - } - - // S{I1} <: AnyStruct{I2} if S is a struct and S conforms to I2 - subType := &IntersectionType{ - LegacyType: structCompositeType, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.True(t, result, "S{I1} should be subtype of AnyStruct{I2} when S conforms to I2") - }) - - t.Run("resource composite intersection not subtype of AnyStruct intersection", func(t *testing.T) { - resourceCompositeType := &CompositeType{ - Location: location, - Identifier: "R", - Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - } - - // R{I1} is NOT <: AnyStruct{I2} because R is a resource, not a struct - subType := &IntersectionType{ - LegacyType: resourceCompositeType, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.False(t, result, "R{I1} should NOT be subtype of AnyStruct{I2} (resource vs struct)") - }) - - t.Run("composite intersection is subtype of AnyType intersection with conformance", func(t *testing.T) { - structCompositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - ExplicitInterfaceConformances: []*InterfaceType{interfaceType1, interfaceType2}, - } - - // S{I1} <: Any{I2} if S conforms to I2 - subType := &IntersectionType{ - LegacyType: structCompositeType, - Types: []*InterfaceType{interfaceType1}, - } - superType := &IntersectionType{ - LegacyType: AnyType, - Types: []*InterfaceType{interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, superType) - assert.True(t, result, "S{I1} should be subtype of Any{I2} when S conforms to I2") - }) - - // Test intersection subtype with IntersectionType interface supertype - t.Run("intersection type subtype of interface type", func(t *testing.T) { - // {I1, I2} <: I1 when I1 is in the intersection set - subType := &IntersectionType{ - Types: []*InterfaceType{interfaceType1, interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, interfaceType1) - assert.True(t, result, "{I1, I2} should be a subtype of I1") - }) - - t.Run("intersection type not subtype of interface not in set", func(t *testing.T) { - interfaceType3 := &InterfaceType{ - Location: location, - Identifier: "I3", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // {I1, I2} is NOT <: I3 when I3 is not in the intersection set - subType := &IntersectionType{ - Types: []*InterfaceType{interfaceType1, interfaceType2}, - } - result := checkSubTypeFunctions(t, subType, interfaceType3) - assert.False(t, result, "{I1, I2} should NOT be a subtype of I3") - }) - - t.Run("parameterized type not subtype of intersection", func(t *testing.T) { - // Capability is NOT <: {I1} - capType := &CapabilityType{ - BorrowType: IntType, - } - superType := &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{}, - } - result := checkSubTypeFunctions(t, capType, superType) - assert.False(t, result, "Capability should NOT be subtype of {I1}") - }) - }) - - t.Run("CompositeType", func(t *testing.T) { - t.Parallel() - - location := common.NewStringLocation(nil, "test") - - composite1 := &CompositeType{ - Location: location, - Identifier: "S1", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - composite2 := &CompositeType{ - Location: location, - Identifier: "S2", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - t.Run("different composite types are not subtypes", func(t *testing.T) { - result := checkSubTypeFunctions(t, composite1, composite2) - assert.False(t, result, "different composite types should NOT be subtypes") - }) - - t.Run("non-composite is not subtype of composite", func(t *testing.T) { - result := checkSubTypeFunctions(t, IntType, composite1) - assert.False(t, result, "Int should NOT be a subtype of composite") - }) - - // Tests for IntersectionType subtype with CompositeType supertype - t.Run("intersection with nil legacy type not subtype of composite", func(t *testing.T) { - interfaceType := &InterfaceType{ - Location: location, - Identifier: "I", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // {I} is NOT <: S statically - subType := &IntersectionType{ - Types: []*InterfaceType{interfaceType}, - } - result := checkSubTypeFunctions(t, subType, composite1) - assert.False(t, result, "{I} should NOT be statically a subtype of S") - }) - - t.Run("intersection with Any* legacy type not subtype of composite", func(t *testing.T) { - interfaceType := &InterfaceType{ - Location: location, - Identifier: "I", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // AnyStruct{I} is NOT <: S statically - subType := &IntersectionType{ - LegacyType: AnyStructType, - Types: []*InterfaceType{interfaceType}, - } - result := checkSubTypeFunctions(t, subType, composite1) - assert.False(t, result, "AnyStruct{I} should NOT be statically a subtype of S") - }) - - t.Run("intersection with matching composite legacy type", func(t *testing.T) { - interfaceType := &InterfaceType{ - Location: location, - Identifier: "I", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // S{I} <: S (owner may freely unrestrict) - subType := &IntersectionType{ - LegacyType: composite1, - Types: []*InterfaceType{interfaceType}, - } - result := checkSubTypeFunctions(t, subType, composite1) - assert.True(t, result, "S{I} should be a subtype of S (unrestrict)") - }) - - t.Run("intersection with different composite legacy type", func(t *testing.T) { - interfaceType := &InterfaceType{ - Location: location, - Identifier: "I", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // S1{I} is NOT <: S2 when S1 != S2 - subType := &IntersectionType{ - LegacyType: composite2, - Types: []*InterfaceType{interfaceType}, - } - result := checkSubTypeFunctions(t, subType, composite1) - assert.False(t, result, "S1{I} should NOT be a subtype of S2") - }) - - t.Run("intersection with interface legacy type not subtype of composite", func(t *testing.T) { - interfaceType := &InterfaceType{ - Location: location, - Identifier: "I", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // I{I} is NOT <: S (interface legacy type in intersection) - subType := &IntersectionType{ - LegacyType: interfaceType, - Types: []*InterfaceType{interfaceType}, - } - result := checkSubTypeFunctions(t, subType, composite1) - assert.False(t, result, "intersection with interface legacy type should NOT be subtype of composite") - }) - }) - - t.Run("InterfaceType", func(t *testing.T) { - t.Parallel() - - location := common.NewStringLocation(nil, "test") - - interfaceType := &InterfaceType{ - Location: location, - Identifier: "I", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - t.Run("composite conforming to interface", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // Set up conformance - compositeType.ExplicitInterfaceConformances = []*InterfaceType{interfaceType} - - result := checkSubTypeFunctions(t, compositeType, interfaceType) - assert.True(t, result, "composite conforming to interface should be a subtype") - }) - - t.Run("composite NOT conforming to interface", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "S", - Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // No conformance set - - result := checkSubTypeFunctions(t, compositeType, interfaceType) - assert.False(t, result, "composite NOT conforming to interface should NOT be a subtype") - }) - - t.Run("composite with wrong kind", func(t *testing.T) { - compositeType := &CompositeType{ - Location: location, - Identifier: "R", - Kind: common.CompositeKindResource, // Different kind - Members: &StringMemberOrderedMap{}, - } - - result := checkSubTypeFunctions(t, compositeType, interfaceType) - assert.False(t, result, "composite with different kind should NOT be a subtype") - }) - - t.Run("interface subtype of interface", func(t *testing.T) { - interface1 := &InterfaceType{ - Location: location, - Identifier: "I1", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - interface2 := &InterfaceType{ - Location: location, - Identifier: "I2", - CompositeKind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, - } - - // Set up conformance: I1 conforms to I2 - interface1.ExplicitInterfaceConformances = []*InterfaceType{interface2} - - result := checkSubTypeFunctions(t, interface1, interface2) - assert.True(t, result, "interface conforming to another interface should be a subtype") - }) - - t.Run("non-conforming type not subtype of interface", func(t *testing.T) { - // Test fallback to IsParameterizedSubType for types that don't match the specific cases - // Int is NOT <: InterfaceType - result := checkSubTypeFunctions(t, IntType, interfaceType) - assert.False(t, result, "Int should NOT be a subtype of interface") - }) - }) - - t.Run("ParameterizedType", func(t *testing.T) { - t.Parallel() - - // This test uses CapabilityType which is a ParameterizedType - - t.Run("capability with matching borrow type", func(t *testing.T) { - capability1 := &CapabilityType{ - BorrowType: IntType, - } - capability2 := &CapabilityType{ - BorrowType: NumberType, - } - - result := checkSubTypeFunctions(t, capability1, capability2) - assert.True(t, result, "Capability should be a subtype of Capability") - }) - - t.Run("capability with non-matching borrow type", func(t *testing.T) { - capability1 := &CapabilityType{ - BorrowType: IntType, - } - capability2 := &CapabilityType{ - BorrowType: StringType, - } - - result := checkSubTypeFunctions(t, capability1, capability2) - assert.False(t, result, "Capability should NOT be a subtype of Capability") - }) - - t.Run("non-capability is not subtype of capability", func(t *testing.T) { - capability := &CapabilityType{ - BorrowType: IntType, - } - - result := checkSubTypeFunctions(t, IntType, capability) - // This may pass or fail depending on IsParameterizedSubType fallback - // The function checks if IntType's base type is a subtype of capability - assert.False(t, result, "Int should NOT be a subtype of Capability") - }) - - t.Run("parameterized type with nil BaseType", func(t *testing.T) { - // Capability with nil BorrowType has nil BaseType - nilCapability := &CapabilityType{ - BorrowType: nil, - } - - // Int is NOT <: Capability - result := checkSubTypeFunctions(t, IntType, nilCapability) - assert.False(t, result, "Int should NOT be a subtype of Capability with nil BorrowType") - }) - - t.Run("parameterized type with base types not matching", func(t *testing.T) { - // This tests the case where base types don't match - capability := &CapabilityType{ - BorrowType: IntType, - } - inclusiveRange := &InclusiveRangeType{ - MemberType: IntType, - } - - // The base types need to be subtypes first - result := checkSubTypeFunctions(t, capability, inclusiveRange) - assert.False(t, result, "base types must match for parameterized subtypes") - }) - }) - - t.Run("unhandled type", func(t *testing.T) { - // TransactionType doesn't match any special case in the switch statement - // so this falls back to the default-case. - result := checkSubTypeFunctions(t, IntType, &TransactionType{}) - assert.False(t, result, "Int should NOT be a subtype of TransactionType") - }) -} diff --git a/sema/type.go b/sema/type.go index da520a3fb9..9c1a131463 100644 --- a/sema/type.go +++ b/sema/type.go @@ -7491,7 +7491,7 @@ func IsSubType(subType Type, superType Type) bool { return true } - return checkSubTypeWithoutEquality(subType, superType) + return CheckSubTypeWithoutEquality(subType, superType) } // IsSameTypeKind determines if the given subtype belongs to the @@ -7519,16 +7519,16 @@ func IsProperSubType(subType Type, superType Type) bool { return false } - return checkSubTypeWithoutEquality(subType, superType) + return CheckSubTypeWithoutEquality(subType, superType) } -// checkSubTypeWithoutEquality determines if the given subtype +// CheckSubTypeWithoutEquality determines if the given subtype // is a subtype of the given supertype, BUT it does NOT check // the equality of the two types, so does NOT return a specific // value when the two types are equal or are not. // // Consider using IsSubType or IsProperSubType -func checkSubTypeWithoutEquality(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality(subType Type, superType Type) bool { if subType == NeverType { return true diff --git a/tools/subtype-gen/constants.go b/tools/subtype-gen/constants.go index a9e76da4c9..4f2e795c59 100644 --- a/tools/subtype-gen/constants.go +++ b/tools/subtype-gen/constants.go @@ -36,7 +36,7 @@ const ( FieldNameReferencedType = "ReferencedType" - subtypeCheckFuncName = "checkSubTypeWithoutEquality_gen" + subtypeCheckFuncName = "CheckSubTypeWithoutEquality_gen" subTypeVarName = "subType" superTypeVarName = "superType" sourceVarName = "source" From 61f779baddf4531716ff50c5ab09dc043cdf04b1 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Nov 2025 10:37:55 -0800 Subject: [PATCH 2/7] Add storable type support --- interpreter/primitivestatictype.go | 10 +- interpreter/primitivestatictype_string.go | 188 +++++++++++----------- interpreter/subtype_check.gen.go | 3 + interpreter/subtype_check.go | 8 +- interpreter/type_check_gen/main.go | 3 - runtime/subtype_check_test.go | 2 +- sema/subtype_check.gen.go | 2 +- sema/subtype_check.go | 4 + tools/subtype-gen/generator.go | 22 ++- 9 files changed, 128 insertions(+), 114 deletions(-) diff --git a/interpreter/primitivestatictype.go b/interpreter/primitivestatictype.go index 468fee0bfe..ed428f1ee6 100644 --- a/interpreter/primitivestatictype.go +++ b/interpreter/primitivestatictype.go @@ -86,7 +86,7 @@ const ( PrimitiveStaticTypeAnyResourceAttachment PrimitiveStaticTypeAnyStructAttachment PrimitiveStaticTypeHashableStruct - _ + PrimitiveStaticTypeStorable _ _ @@ -273,7 +273,8 @@ func (t PrimitiveStaticType) elementSize() uint { PrimitiveStaticTypeAny, PrimitiveStaticTypeAnyStructAttachment, PrimitiveStaticTypeAnyResourceAttachment, - PrimitiveStaticTypeHashableStruct: + PrimitiveStaticTypeHashableStruct, + PrimitiveStaticTypeStorable: return UnknownElementSize case PrimitiveStaticTypeVoid: @@ -511,6 +512,9 @@ func (t PrimitiveStaticType) SemaType() sema.Type { case PrimitiveStaticTypeBlock: return sema.BlockType + case PrimitiveStaticTypeStorable: + return sema.StorableType + // Number case PrimitiveStaticTypeNumber: @@ -881,6 +885,8 @@ func ConvertSemaToPrimitiveStaticType( typ = PrimitiveStaticTypeStorageCapabilityController case sema.AccountCapabilityControllerType: typ = PrimitiveStaticTypeAccountCapabilityController + case sema.StorableType: + typ = PrimitiveStaticTypeStorable case sema.AccountType: typ = PrimitiveStaticTypeAccount diff --git a/interpreter/primitivestatictype_string.go b/interpreter/primitivestatictype_string.go index 0c69323720..c0720c87c6 100644 --- a/interpreter/primitivestatictype_string.go +++ b/interpreter/primitivestatictype_string.go @@ -23,6 +23,7 @@ func _() { _ = x[PrimitiveStaticTypeAnyResourceAttachment-12] _ = x[PrimitiveStaticTypeAnyStructAttachment-13] _ = x[PrimitiveStaticTypeHashableStruct-14] + _ = x[PrimitiveStaticTypeStorable-15] _ = x[PrimitiveStaticTypeNumber-18] _ = x[PrimitiveStaticTypeSignedNumber-19] _ = x[PrimitiveStaticTypeInteger-24] @@ -117,7 +118,7 @@ func _() { _ = x[PrimitiveStaticType_Count-152] } -const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockAnyResourceAttachmentAnyStructAttachmentHashableStructNumberSignedNumberIntegerSignedIntegerFixedSizeUnsignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Word128Word256Fix64Fix128UFix64UFix128PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxStorageCapabilityControllerAccountCapabilityControllerAuthAccountStorageCapabilitiesAuthAccountAccountCapabilitiesAuthAccountCapabilitiesPublicAccountCapabilitiesAccountAccount_ContractsAccount_KeysAccount_InboxAccount_StorageCapabilitiesAccount_AccountCapabilitiesAccount_CapabilitiesAccount_StorageMutateInsertRemoveIdentityStorageSaveValueLoadValueCopyValueBorrowValueContractsAddContractUpdateContractRemoveContractKeysAddKeyRevokeKeyInboxPublishInboxCapabilityUnpublishInboxCapabilityClaimInboxCapabilityCapabilitiesStorageCapabilitiesAccountCapabilitiesPublishCapabilityUnpublishCapabilityGetStorageCapabilityControllerIssueStorageCapabilityControllerGetAccountCapabilityControllerIssueAccountCapabilityControllerCapabilitiesMappingAccountMapping_Count" +const _PrimitiveStaticType_name = "UnknownVoidAnyNeverAnyStructAnyResourceBoolAddressStringCharacterMetaTypeBlockAnyResourceAttachmentAnyStructAttachmentHashableStructStorableNumberSignedNumberIntegerSignedIntegerFixedSizeUnsignedIntegerFixedPointSignedFixedPointIntInt8Int16Int32Int64Int128Int256UIntUInt8UInt16UInt32UInt64UInt128UInt256Word8Word16Word32Word64Word128Word256Fix64Fix128UFix64UFix128PathCapabilityStoragePathCapabilityPathPublicPathPrivatePathAuthAccountPublicAccountDeployedContractAuthAccountContractsPublicAccountContractsAuthAccountKeysPublicAccountKeysAccountKeyAuthAccountInboxStorageCapabilityControllerAccountCapabilityControllerAuthAccountStorageCapabilitiesAuthAccountAccountCapabilitiesAuthAccountCapabilitiesPublicAccountCapabilitiesAccountAccount_ContractsAccount_KeysAccount_InboxAccount_StorageCapabilitiesAccount_AccountCapabilitiesAccount_CapabilitiesAccount_StorageMutateInsertRemoveIdentityStorageSaveValueLoadValueCopyValueBorrowValueContractsAddContractUpdateContractRemoveContractKeysAddKeyRevokeKeyInboxPublishInboxCapabilityUnpublishInboxCapabilityClaimInboxCapabilityCapabilitiesStorageCapabilitiesAccountCapabilitiesPublishCapabilityUnpublishCapabilityGetStorageCapabilityControllerIssueStorageCapabilityControllerGetAccountCapabilityControllerIssueAccountCapabilityControllerCapabilitiesMappingAccountMapping_Count" var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 0: _PrimitiveStaticType_name[0:7], @@ -135,98 +136,99 @@ var _PrimitiveStaticType_map = map[PrimitiveStaticType]string{ 12: _PrimitiveStaticType_name[78:99], 13: _PrimitiveStaticType_name[99:118], 14: _PrimitiveStaticType_name[118:132], - 18: _PrimitiveStaticType_name[132:138], - 19: _PrimitiveStaticType_name[138:150], - 24: _PrimitiveStaticType_name[150:157], - 25: _PrimitiveStaticType_name[157:170], - 26: _PrimitiveStaticType_name[170:194], - 30: _PrimitiveStaticType_name[194:204], - 31: _PrimitiveStaticType_name[204:220], - 36: _PrimitiveStaticType_name[220:223], - 37: _PrimitiveStaticType_name[223:227], - 38: _PrimitiveStaticType_name[227:232], - 39: _PrimitiveStaticType_name[232:237], - 40: _PrimitiveStaticType_name[237:242], - 41: _PrimitiveStaticType_name[242:248], - 42: _PrimitiveStaticType_name[248:254], - 44: _PrimitiveStaticType_name[254:258], - 45: _PrimitiveStaticType_name[258:263], - 46: _PrimitiveStaticType_name[263:269], - 47: _PrimitiveStaticType_name[269:275], - 48: _PrimitiveStaticType_name[275:281], - 49: _PrimitiveStaticType_name[281:288], - 50: _PrimitiveStaticType_name[288:295], - 53: _PrimitiveStaticType_name[295:300], - 54: _PrimitiveStaticType_name[300:306], - 55: _PrimitiveStaticType_name[306:312], - 56: _PrimitiveStaticType_name[312:318], - 57: _PrimitiveStaticType_name[318:325], - 58: _PrimitiveStaticType_name[325:332], - 64: _PrimitiveStaticType_name[332:337], - 65: _PrimitiveStaticType_name[337:343], - 72: _PrimitiveStaticType_name[343:349], - 73: _PrimitiveStaticType_name[349:356], - 76: _PrimitiveStaticType_name[356:360], - 77: _PrimitiveStaticType_name[360:370], - 78: _PrimitiveStaticType_name[370:381], - 79: _PrimitiveStaticType_name[381:395], - 80: _PrimitiveStaticType_name[395:405], - 81: _PrimitiveStaticType_name[405:416], - 90: _PrimitiveStaticType_name[416:427], - 91: _PrimitiveStaticType_name[427:440], - 92: _PrimitiveStaticType_name[440:456], - 93: _PrimitiveStaticType_name[456:476], - 94: _PrimitiveStaticType_name[476:498], - 95: _PrimitiveStaticType_name[498:513], - 96: _PrimitiveStaticType_name[513:530], - 97: _PrimitiveStaticType_name[530:540], - 98: _PrimitiveStaticType_name[540:556], - 99: _PrimitiveStaticType_name[556:583], - 100: _PrimitiveStaticType_name[583:610], - 101: _PrimitiveStaticType_name[610:640], - 102: _PrimitiveStaticType_name[640:670], - 103: _PrimitiveStaticType_name[670:693], - 104: _PrimitiveStaticType_name[693:718], - 105: _PrimitiveStaticType_name[718:725], - 106: _PrimitiveStaticType_name[725:742], - 107: _PrimitiveStaticType_name[742:754], - 108: _PrimitiveStaticType_name[754:767], - 109: _PrimitiveStaticType_name[767:794], - 110: _PrimitiveStaticType_name[794:821], - 111: _PrimitiveStaticType_name[821:841], - 112: _PrimitiveStaticType_name[841:856], - 118: _PrimitiveStaticType_name[856:862], - 119: _PrimitiveStaticType_name[862:868], - 120: _PrimitiveStaticType_name[868:874], - 121: _PrimitiveStaticType_name[874:882], - 125: _PrimitiveStaticType_name[882:889], - 126: _PrimitiveStaticType_name[889:898], - 127: _PrimitiveStaticType_name[898:907], - 128: _PrimitiveStaticType_name[907:916], - 129: _PrimitiveStaticType_name[916:927], - 130: _PrimitiveStaticType_name[927:936], - 131: _PrimitiveStaticType_name[936:947], - 132: _PrimitiveStaticType_name[947:961], - 133: _PrimitiveStaticType_name[961:975], - 134: _PrimitiveStaticType_name[975:979], - 135: _PrimitiveStaticType_name[979:985], - 136: _PrimitiveStaticType_name[985:994], - 137: _PrimitiveStaticType_name[994:999], - 138: _PrimitiveStaticType_name[999:1021], - 139: _PrimitiveStaticType_name[1021:1045], - 140: _PrimitiveStaticType_name[1045:1065], - 141: _PrimitiveStaticType_name[1065:1077], - 142: _PrimitiveStaticType_name[1077:1096], - 143: _PrimitiveStaticType_name[1096:1115], - 144: _PrimitiveStaticType_name[1115:1132], - 145: _PrimitiveStaticType_name[1132:1151], - 146: _PrimitiveStaticType_name[1151:1181], - 147: _PrimitiveStaticType_name[1181:1213], - 148: _PrimitiveStaticType_name[1213:1243], - 149: _PrimitiveStaticType_name[1243:1275], - 150: _PrimitiveStaticType_name[1275:1294], - 151: _PrimitiveStaticType_name[1294:1308], - 152: _PrimitiveStaticType_name[1308:1314], + 15: _PrimitiveStaticType_name[132:140], + 18: _PrimitiveStaticType_name[140:146], + 19: _PrimitiveStaticType_name[146:158], + 24: _PrimitiveStaticType_name[158:165], + 25: _PrimitiveStaticType_name[165:178], + 26: _PrimitiveStaticType_name[178:202], + 30: _PrimitiveStaticType_name[202:212], + 31: _PrimitiveStaticType_name[212:228], + 36: _PrimitiveStaticType_name[228:231], + 37: _PrimitiveStaticType_name[231:235], + 38: _PrimitiveStaticType_name[235:240], + 39: _PrimitiveStaticType_name[240:245], + 40: _PrimitiveStaticType_name[245:250], + 41: _PrimitiveStaticType_name[250:256], + 42: _PrimitiveStaticType_name[256:262], + 44: _PrimitiveStaticType_name[262:266], + 45: _PrimitiveStaticType_name[266:271], + 46: _PrimitiveStaticType_name[271:277], + 47: _PrimitiveStaticType_name[277:283], + 48: _PrimitiveStaticType_name[283:289], + 49: _PrimitiveStaticType_name[289:296], + 50: _PrimitiveStaticType_name[296:303], + 53: _PrimitiveStaticType_name[303:308], + 54: _PrimitiveStaticType_name[308:314], + 55: _PrimitiveStaticType_name[314:320], + 56: _PrimitiveStaticType_name[320:326], + 57: _PrimitiveStaticType_name[326:333], + 58: _PrimitiveStaticType_name[333:340], + 64: _PrimitiveStaticType_name[340:345], + 65: _PrimitiveStaticType_name[345:351], + 72: _PrimitiveStaticType_name[351:357], + 73: _PrimitiveStaticType_name[357:364], + 76: _PrimitiveStaticType_name[364:368], + 77: _PrimitiveStaticType_name[368:378], + 78: _PrimitiveStaticType_name[378:389], + 79: _PrimitiveStaticType_name[389:403], + 80: _PrimitiveStaticType_name[403:413], + 81: _PrimitiveStaticType_name[413:424], + 90: _PrimitiveStaticType_name[424:435], + 91: _PrimitiveStaticType_name[435:448], + 92: _PrimitiveStaticType_name[448:464], + 93: _PrimitiveStaticType_name[464:484], + 94: _PrimitiveStaticType_name[484:506], + 95: _PrimitiveStaticType_name[506:521], + 96: _PrimitiveStaticType_name[521:538], + 97: _PrimitiveStaticType_name[538:548], + 98: _PrimitiveStaticType_name[548:564], + 99: _PrimitiveStaticType_name[564:591], + 100: _PrimitiveStaticType_name[591:618], + 101: _PrimitiveStaticType_name[618:648], + 102: _PrimitiveStaticType_name[648:678], + 103: _PrimitiveStaticType_name[678:701], + 104: _PrimitiveStaticType_name[701:726], + 105: _PrimitiveStaticType_name[726:733], + 106: _PrimitiveStaticType_name[733:750], + 107: _PrimitiveStaticType_name[750:762], + 108: _PrimitiveStaticType_name[762:775], + 109: _PrimitiveStaticType_name[775:802], + 110: _PrimitiveStaticType_name[802:829], + 111: _PrimitiveStaticType_name[829:849], + 112: _PrimitiveStaticType_name[849:864], + 118: _PrimitiveStaticType_name[864:870], + 119: _PrimitiveStaticType_name[870:876], + 120: _PrimitiveStaticType_name[876:882], + 121: _PrimitiveStaticType_name[882:890], + 125: _PrimitiveStaticType_name[890:897], + 126: _PrimitiveStaticType_name[897:906], + 127: _PrimitiveStaticType_name[906:915], + 128: _PrimitiveStaticType_name[915:924], + 129: _PrimitiveStaticType_name[924:935], + 130: _PrimitiveStaticType_name[935:944], + 131: _PrimitiveStaticType_name[944:955], + 132: _PrimitiveStaticType_name[955:969], + 133: _PrimitiveStaticType_name[969:983], + 134: _PrimitiveStaticType_name[983:987], + 135: _PrimitiveStaticType_name[987:993], + 136: _PrimitiveStaticType_name[993:1002], + 137: _PrimitiveStaticType_name[1002:1007], + 138: _PrimitiveStaticType_name[1007:1029], + 139: _PrimitiveStaticType_name[1029:1053], + 140: _PrimitiveStaticType_name[1053:1073], + 141: _PrimitiveStaticType_name[1073:1085], + 142: _PrimitiveStaticType_name[1085:1104], + 143: _PrimitiveStaticType_name[1104:1123], + 144: _PrimitiveStaticType_name[1123:1140], + 145: _PrimitiveStaticType_name[1140:1159], + 146: _PrimitiveStaticType_name[1159:1189], + 147: _PrimitiveStaticType_name[1189:1221], + 148: _PrimitiveStaticType_name[1221:1251], + 149: _PrimitiveStaticType_name[1251:1283], + 150: _PrimitiveStaticType_name[1283:1302], + 151: _PrimitiveStaticType_name[1302:1316], + 152: _PrimitiveStaticType_name[1316:1322], } func (i PrimitiveStaticType) String() string { diff --git a/interpreter/subtype_check.gen.go b/interpreter/subtype_check.gen.go index caef2ec5f3..ac2440cd9a 100644 --- a/interpreter/subtype_check.gen.go +++ b/interpreter/subtype_check.gen.go @@ -52,6 +52,9 @@ func CheckSubTypeWithoutEquality_gen(typeConverter TypeConverter, subType Static return IsSubType(typeConverter, subType, PrimitiveStaticTypeStoragePath) || IsSubType(typeConverter, subType, PrimitiveStaticTypeCapabilityPath) + case PrimitiveStaticTypeStorable: + return IsStorableType(typeConverter, subType) + case PrimitiveStaticTypeCapabilityPath: switch subType { case PrimitiveStaticTypePrivatePath, diff --git a/interpreter/subtype_check.go b/interpreter/subtype_check.go index eb75441703..e24ec1999f 100644 --- a/interpreter/subtype_check.go +++ b/interpreter/subtype_check.go @@ -50,7 +50,8 @@ func IsHashableStructType(typeConverter TypeConverter, typ StaticType) bool { PrimitiveStaticTypeCharacter, PrimitiveStaticTypeString, PrimitiveStaticTypeMetaType, - PrimitiveStaticTypeHashableStruct: + PrimitiveStaticTypeHashableStruct, + PrimitiveStaticTypeAddress: return true default: _, ok := typ.(*CompositeStaticType) @@ -112,6 +113,11 @@ func IsParameterizedSubType(typeConverter TypeConverter, subType StaticType, sup return false } +func IsStorableType(typeConverter TypeConverter, typ StaticType) bool { + semaType := typeConverter.SemaTypeFromStaticType(typ) + return semaType.IsStorable(map[*sema.Member]bool{}) +} + type Equatable[T any] interface { comparable Equal(other T) bool diff --git a/interpreter/type_check_gen/main.go b/interpreter/type_check_gen/main.go index 595acb7659..9bd95f08c7 100644 --- a/interpreter/type_check_gen/main.go +++ b/interpreter/type_check_gen/main.go @@ -62,9 +62,6 @@ func main() { PkgPath: interpreterPkgPath, }, }, - SkipTypes: map[string]struct{}{ - subtypegen.TypePlaceholderStorable: {}, - }, NonPointerTypes: map[string]struct{}{ subtypegen.TypePlaceholderFunction: {}, subtypegen.TypePlaceholderConforming: {}, diff --git a/runtime/subtype_check_test.go b/runtime/subtype_check_test.go index 110464a3fb..a906633d10 100644 --- a/runtime/subtype_check_test.go +++ b/runtime/subtype_check_test.go @@ -200,7 +200,7 @@ func checkSubTypeFunctions(t *testing.T, subType sema.Type, superType sema.Type) subType, superType, result, - generatedSemaResult, + generatedStaticTypeResult, ) return result diff --git a/sema/subtype_check.gen.go b/sema/subtype_check.gen.go index 852f2dd0cd..1b8b1b4526 100644 --- a/sema/subtype_check.gen.go +++ b/sema/subtype_check.gen.go @@ -51,7 +51,7 @@ func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { IsSubType(subType, CapabilityPathType) case StorableType: - return subType.IsStorable(map[*Member]bool{}) + return IsStorableType(subType) case CapabilityPathType: switch subType { diff --git a/sema/subtype_check.go b/sema/subtype_check.go index 96b0fd1166..b6cd294719 100644 --- a/sema/subtype_check.go +++ b/sema/subtype_check.go @@ -135,6 +135,10 @@ func IsParameterizedSubType(subType Type, superType Type) bool { return false } +func IsStorableType(typ Type) bool { + return typ.IsStorable(map[*Member]bool{}) +} + type Equatable[T any] interface { comparable Equal(other T) bool diff --git a/tools/subtype-gen/generator.go b/tools/subtype-gen/generator.go index 8833dc1bd2..c1eee8eb98 100644 --- a/tools/subtype-gen/generator.go +++ b/tools/subtype-gen/generator.go @@ -892,22 +892,18 @@ func (gen *SubTypeCheckGenerator) isHashableStructPredicate(predicate IsHashable } func (gen *SubTypeCheckGenerator) isStorablePredicate(predicate IsStorablePredicate) []dst.Node { - function := &dst.SelectorExpr{ - X: gen.expressionIgnoreNegation(predicate.Expression), - Sel: dst.NewIdent("IsStorable"), - } + args := gen.extraArguments() - argument := &dst.CompositeLit{ - Type: &dst.MapType{ - Key: &dst.StarExpr{ - X: dst.NewIdent("Member"), - }, - Value: dst.NewIdent("bool"), - }, - } + args = append( + args, + gen.expressionIgnoreNegation(predicate.Expression), + ) return []dst.Node{ - gen.callExpression(function, argument), + gen.callExpression( + dst.NewIdent("IsStorableType"), + args..., + ), } } From ac0618be69b7073b804ca2f90f4d7c6181ada0a3 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Nov 2025 11:58:16 -0800 Subject: [PATCH 3/7] Fix rules --- interpreter/subtype_check.gen.go | 4 ++-- interpreter/subtype_check.go | 13 ++++++++----- runtime/subtype_check_test.go | 11 ----------- sema/subtype_check.gen.go | 4 ++-- sema/subtype_check.go | 13 ++++++++----- sema/type.go | 4 ++-- tools/subtype-gen/rules.yaml | 4 ++-- 7 files changed, 24 insertions(+), 29 deletions(-) diff --git a/interpreter/subtype_check.gen.go b/interpreter/subtype_check.gen.go index ac2440cd9a..7132f84528 100644 --- a/interpreter/subtype_check.gen.go +++ b/interpreter/subtype_check.gen.go @@ -212,7 +212,7 @@ func CheckSubTypeWithoutEquality_gen(typeConverter TypeConverter, subType Static switch typedSubTypeLegacyType := typedSubType.LegacyType.(type) { case *CompositeStaticType: - return typedSubTypeLegacyType == typedSuperType + return deepEquals(typedSubTypeLegacyType, typedSuperType) } return false @@ -339,7 +339,7 @@ func CheckSubTypeWithoutEquality_gen(typeConverter TypeConverter, subType Static // When `T != AnyResource && T != AnyStructType && T != Any`: if `T == V`. // `Us` and `Ws` do *not* have to be subsets: // The owner may freely restrict and unrestrict. - return typedSubTypeLegacyType == typedSuperType.LegacyType + return deepEquals(typedSubTypeLegacyType, typedSuperType.LegacyType) } return false diff --git a/interpreter/subtype_check.go b/interpreter/subtype_check.go index e24ec1999f..e85ce0eb89 100644 --- a/interpreter/subtype_check.go +++ b/interpreter/subtype_check.go @@ -123,11 +123,14 @@ type Equatable[T any] interface { Equal(other T) bool } -func deepEquals[T Equatable[T]](source, target T) bool { - var empty T - if source == empty { - return target == empty +func deepEquals[T any, A, B Equatable[T]](source A, target B) bool { + var emptyA A + var emptyB B + if source == emptyA { + return target == emptyB } - return source.Equal(target) + // Convert target to T to pass to source.Equal + targetAsT := any(target).(T) + return source.Equal(targetAsT) } diff --git a/runtime/subtype_check_test.go b/runtime/subtype_check_test.go index a906633d10..4ffb8ad0df 100644 --- a/runtime/subtype_check_test.go +++ b/runtime/subtype_check_test.go @@ -155,17 +155,9 @@ func checkSubTypeFunctions(t *testing.T, subType sema.Type, superType sema.Type) elaboration.SetEntitlementType(entitlementType.ID(), entitlementType) - //addToElaboration := func(typ sema.Type) { - // if compositeType, ok := typ.(*sema.CompositeType); ok { - // elaboration.SetCompositeType(compositeType.ID(), compositeType) - // } - //} - subStaticType := interpreter.ConvertSemaToStaticType(nil, subType) - //addToElaboration(subType) superStaticType := interpreter.ConvertSemaToStaticType(nil, superType) - //addToElaboration(superType) inter, err := interpreter.NewInterpreter( nil, @@ -174,9 +166,6 @@ func checkSubTypeFunctions(t *testing.T, subType sema.Type, superType sema.Type) CompositeTypeHandler: func(location common.Location, typeID interpreter.TypeID) *sema.CompositeType { return elaboration.CompositeType(typeID) }, - //InterfaceTypeHandler: func(location common.Location, typeID interpreter.TypeID) *sema.InterfaceType { - // return typeMappings[typeID].(*sema.InterfaceType) - //}, ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { return interpreter.VirtualImport{ Elaboration: elaboration, diff --git a/sema/subtype_check.gen.go b/sema/subtype_check.gen.go index 1b8b1b4526..a8d13fae19 100644 --- a/sema/subtype_check.gen.go +++ b/sema/subtype_check.gen.go @@ -210,7 +210,7 @@ func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { switch typedSubTypeLegacyType := typedSubType.LegacyType.(type) { case *CompositeType: - return typedSubTypeLegacyType == typedSuperType + return deepEquals(typedSubTypeLegacyType, typedSuperType) } return false @@ -331,7 +331,7 @@ func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { // When `T != AnyResource && T != AnyStructType && T != Any`: if `T == V`. // `Us` and `Ws` do *not* have to be subsets: // The owner may freely restrict and unrestrict. - return typedSubTypeLegacyType == typedSuperType.LegacyType + return deepEquals(typedSubTypeLegacyType, typedSuperType.LegacyType) } return false diff --git a/sema/subtype_check.go b/sema/subtype_check.go index b6cd294719..c84933c5be 100644 --- a/sema/subtype_check.go +++ b/sema/subtype_check.go @@ -144,11 +144,14 @@ type Equatable[T any] interface { Equal(other T) bool } -func deepEquals[T Equatable[T]](source, target T) bool { - var empty T - if source == empty { - return target == empty +func deepEquals[T any, A, B Equatable[T]](source A, target B) bool { + var emptyA A + var emptyB B + if source == emptyA { + return target == emptyB } - return source.Equal(target) + // Convert target to T to pass to source.Equal + targetAsT := any(target).(T) + return source.Equal(targetAsT) } diff --git a/sema/type.go b/sema/type.go index 9c1a131463..8197490785 100644 --- a/sema/type.go +++ b/sema/type.go @@ -7870,7 +7870,7 @@ func CheckSubTypeWithoutEquality(subType Type, superType Type) bool { // `Us` and `Ws` do *not* have to be subsets: // The owner may freely restrict and unrestrict. - return intersectionSubType == intersectionSuperType + return intersectionSubType.Equal(intersectionSuperType) } case *CompositeType: @@ -7920,7 +7920,7 @@ func CheckSubTypeWithoutEquality(subType Type, superType Type) bool { // // The owner may freely unrestrict. - return intersectionSubType == typedSuperType + return intersectionSubType.Equal(typedSuperType) } case *CompositeType: diff --git a/tools/subtype-gen/rules.yaml b/tools/subtype-gen/rules.yaml index 69a4748ebd..8b2434219e 100644 --- a/tools/subtype-gen/rules.yaml +++ b/tools/subtype-gen/rules.yaml @@ -242,7 +242,7 @@ rules: - mustType: source: sub.LegacyType type: CompositeType - - equals: + - deepEquals: source: sub.LegacyType target: super - and: @@ -410,7 +410,7 @@ rules: # When `T != AnyResource && T != AnyStructType && T != Any`: if `T == V`. # `Us` and `Ws` do *not* have to be subsets: # The owner may freely restrict and unrestrict. - - equals: + - deepEquals: source: sub.LegacyType target: super.LegacyType From 407ea2e336b245255f8d1a5c599f3139383d7572 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Nov 2025 12:21:09 -0800 Subject: [PATCH 4/7] Fix tests --- encoding/ccf/simpletype_test.go | 4 +++- interpreter/memory_metering_test.go | 1 + interpreter/statictype_test.go | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/encoding/ccf/simpletype_test.go b/encoding/ccf/simpletype_test.go index 528df7d2c7..f0a25dd075 100644 --- a/encoding/ccf/simpletype_test.go +++ b/encoding/ccf/simpletype_test.go @@ -52,7 +52,9 @@ func TestTypeConversion(t *testing.T) { } for ty := interpreter.PrimitiveStaticType(1); ty < interpreter.PrimitiveStaticType_Count; ty++ { - if !ty.IsDefined() || ty.IsDeprecated() { //nolint:staticcheck + if !ty.IsDefined() || + ty.IsDeprecated() || //nolint:staticcheck + ty == interpreter.PrimitiveStaticTypeStorable { continue } diff --git a/interpreter/memory_metering_test.go b/interpreter/memory_metering_test.go index 6168fe629e..f244441bfe 100644 --- a/interpreter/memory_metering_test.go +++ b/interpreter/memory_metering_test.go @@ -9730,6 +9730,7 @@ func TestInterpretStaticTypeStringConversion(t *testing.T) { switch primitiveStaticType { case interpreter.PrimitiveStaticTypeAny, + interpreter.PrimitiveStaticTypeStorable, interpreter.PrimitiveStaticTypeUnknown, interpreter.PrimitiveStaticType_Count: continue diff --git a/interpreter/statictype_test.go b/interpreter/statictype_test.go index 6cb1bc1138..248d77dda2 100644 --- a/interpreter/statictype_test.go +++ b/interpreter/statictype_test.go @@ -1621,6 +1621,11 @@ func TestStaticTypeConversion(t *testing.T) { ElementType: PrimitiveStaticTypeInt, }, }, + { + name: "Storable", + semaType: sema.StorableType, + staticType: PrimitiveStaticTypeStorable, + }, // Deprecated primitive static types, only exist for migration purposes { name: "AuthAccount", From 4f487b5995b7d1f9355fd16e62d061728c528f80 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 13 Nov 2025 14:17:23 -0800 Subject: [PATCH 5/7] Fix subtype check generator test --- Makefile | 8 +++++++- runtime/subtype_check_test.go | 2 +- tools/subtype-gen/testdata/and_predicate_test.golden.go | 2 +- .../testdata/complex_nested_and_or_test.golden.go | 2 +- .../complex_nested_and_or_with_not_test.golden.go | 2 +- tools/subtype-gen/testdata/complex_type_test.golden.go | 2 +- .../testdata/deep_equals_predicate_test.golden.go | 2 +- .../testdata/deeply_nested_predicates_test.golden.go | 2 +- tools/subtype-gen/testdata/default_rule_test.golden.go | 2 +- .../subtype-gen/testdata/equals_predicate_test.golden.go | 2 +- .../subtype-gen/testdata/for_all_predicate_test.golden.go | 2 +- .../testdata/is_attachment_predicate_test.golden.go | 2 +- .../testdata/is_hashable_struct_predicate_test.golden.go | 2 +- .../is_intersection_subset_predicate_test.golden.go | 2 +- .../is_parameterized_subtype_predicate_test.golden.go | 2 +- .../testdata/is_resource_predicate_test.golden.go | 2 +- .../testdata/is_storable_predicate_test.golden.go | 4 ++-- .../testdata/must_type_predicate_test.golden.go | 2 +- tools/subtype-gen/testdata/never_predicate_test.golden.go | 2 +- tools/subtype-gen/testdata/not_predicate_test.golden.go | 2 +- .../testdata/not_with_multiple_levels_test.golden.go | 2 +- .../subtype-gen/testdata/oneof_expression_test.golden.go | 2 +- tools/subtype-gen/testdata/oneof_with_not_test.golden.go | 2 +- tools/subtype-gen/testdata/or_predicate_test.golden.go | 2 +- .../subtype-gen/testdata/permits_predicate_test.golden.go | 2 +- .../testdata/return_covariant_predicate_test.golden.go | 2 +- .../testdata/set_contains_predicate_test.golden.go | 2 +- .../testdata/simple_and_complex_type_test.golden.go | 2 +- tools/subtype-gen/testdata/simple_type_test.golden.go | 2 +- .../subtype-gen/testdata/subtype_predicate_test.golden.go | 2 +- 30 files changed, 37 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 563898b0fd..496611b325 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ build-commands: ./cmd/parse/parse ./cmd/parse/parse.wasm ./cmd/check/check ./cmd build-tools: build-analysis build-get-contracts build-compatibility-check .PHONY: test-tools -test-tools: test-analysis test-compatibility-check +test-tools: test-analysis test-compatibility-check test-subtype-gen ## Analysis tool @@ -80,6 +80,12 @@ build-compatibility-check: test-compatibility-check: (cd ./tools/compatibility-check && go test .) +## Subtyping generator tool + +.PHONY: test-subtype-gen +test-subtype-gen: + (cd ./tools/subtype-gen && go test .) + # Testing TEST_PKGS := $(shell go list ./... | grep -Ev '/cmd|/analysis|/tools') diff --git a/runtime/subtype_check_test.go b/runtime/subtype_check_test.go index 4ffb8ad0df..3510a770e7 100644 --- a/runtime/subtype_check_test.go +++ b/runtime/subtype_check_test.go @@ -119,7 +119,7 @@ var resourceTypeWithConformance2 = &sema.CompositeType{ ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceType1, interfaceType2}, } -// checkSubTypeFunctions calls both checkSubTypeWithoutEquality and checkSubTypeWithoutEquality_gen +// checkSubTypeFunctions calls both checkSubTypeWithoutEquality and CheckSubTypeWithoutEquality_gen // and asserts they produce the same result func checkSubTypeFunctions(t *testing.T, subType sema.Type, superType sema.Type) bool { result := sema.CheckSubTypeWithoutEquality(subType, superType) diff --git a/tools/subtype-gen/testdata/and_predicate_test.golden.go b/tools/subtype-gen/testdata/and_predicate_test.golden.go index e9ad5e14a3..e2541ba27e 100644 --- a/tools/subtype-gen/testdata/and_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/and_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/complex_nested_and_or_test.golden.go b/tools/subtype-gen/testdata/complex_nested_and_or_test.golden.go index 6bfeddeadc..350015673c 100644 --- a/tools/subtype-gen/testdata/complex_nested_and_or_test.golden.go +++ b/tools/subtype-gen/testdata/complex_nested_and_or_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/complex_nested_and_or_with_not_test.golden.go b/tools/subtype-gen/testdata/complex_nested_and_or_with_not_test.golden.go index bd39d79405..43952be2ca 100644 --- a/tools/subtype-gen/testdata/complex_nested_and_or_with_not_test.golden.go +++ b/tools/subtype-gen/testdata/complex_nested_and_or_with_not_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/complex_type_test.golden.go b/tools/subtype-gen/testdata/complex_type_test.golden.go index 788f31a7bc..b9adb66edd 100644 --- a/tools/subtype-gen/testdata/complex_type_test.golden.go +++ b/tools/subtype-gen/testdata/complex_type_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/deep_equals_predicate_test.golden.go b/tools/subtype-gen/testdata/deep_equals_predicate_test.golden.go index 512abefc2b..18bbcaf7b2 100644 --- a/tools/subtype-gen/testdata/deep_equals_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/deep_equals_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/deeply_nested_predicates_test.golden.go b/tools/subtype-gen/testdata/deeply_nested_predicates_test.golden.go index 830b6f1d6b..f72bcb1e6b 100644 --- a/tools/subtype-gen/testdata/deeply_nested_predicates_test.golden.go +++ b/tools/subtype-gen/testdata/deeply_nested_predicates_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/default_rule_test.golden.go b/tools/subtype-gen/testdata/default_rule_test.golden.go index b4e3f9e5df..774aabbd42 100644 --- a/tools/subtype-gen/testdata/default_rule_test.golden.go +++ b/tools/subtype-gen/testdata/default_rule_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/equals_predicate_test.golden.go b/tools/subtype-gen/testdata/equals_predicate_test.golden.go index 87e2d65035..839079a4a3 100644 --- a/tools/subtype-gen/testdata/equals_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/equals_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/for_all_predicate_test.golden.go b/tools/subtype-gen/testdata/for_all_predicate_test.golden.go index 64d63f316c..8e4fd4d9e7 100644 --- a/tools/subtype-gen/testdata/for_all_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/for_all_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/is_attachment_predicate_test.golden.go b/tools/subtype-gen/testdata/is_attachment_predicate_test.golden.go index 5f224baaba..25e67e9ab4 100644 --- a/tools/subtype-gen/testdata/is_attachment_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/is_attachment_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/is_hashable_struct_predicate_test.golden.go b/tools/subtype-gen/testdata/is_hashable_struct_predicate_test.golden.go index 362c09132d..a1b43eb649 100644 --- a/tools/subtype-gen/testdata/is_hashable_struct_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/is_hashable_struct_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/is_intersection_subset_predicate_test.golden.go b/tools/subtype-gen/testdata/is_intersection_subset_predicate_test.golden.go index 19d11d4b37..62589df58c 100644 --- a/tools/subtype-gen/testdata/is_intersection_subset_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/is_intersection_subset_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/is_parameterized_subtype_predicate_test.golden.go b/tools/subtype-gen/testdata/is_parameterized_subtype_predicate_test.golden.go index 7a498a985f..3ca9b2407f 100644 --- a/tools/subtype-gen/testdata/is_parameterized_subtype_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/is_parameterized_subtype_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/is_resource_predicate_test.golden.go b/tools/subtype-gen/testdata/is_resource_predicate_test.golden.go index 2f5c2f8a7a..a0df251d46 100644 --- a/tools/subtype-gen/testdata/is_resource_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/is_resource_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/is_storable_predicate_test.golden.go b/tools/subtype-gen/testdata/is_storable_predicate_test.golden.go index 49c8e25776..8ac31202c4 100644 --- a/tools/subtype-gen/testdata/is_storable_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/is_storable_predicate_test.golden.go @@ -19,14 +19,14 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } switch superType { case StorableType: - return subType.IsStorable(map[*Member]bool{}) + return IsStorableType(subType) } diff --git a/tools/subtype-gen/testdata/must_type_predicate_test.golden.go b/tools/subtype-gen/testdata/must_type_predicate_test.golden.go index 3fc06f30ce..8cf59cd39f 100644 --- a/tools/subtype-gen/testdata/must_type_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/must_type_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/never_predicate_test.golden.go b/tools/subtype-gen/testdata/never_predicate_test.golden.go index d1d487dd72..5b66d7e121 100644 --- a/tools/subtype-gen/testdata/never_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/never_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/not_predicate_test.golden.go b/tools/subtype-gen/testdata/not_predicate_test.golden.go index 5d2f243a3a..afeee7006f 100644 --- a/tools/subtype-gen/testdata/not_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/not_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/not_with_multiple_levels_test.golden.go b/tools/subtype-gen/testdata/not_with_multiple_levels_test.golden.go index d1de1d1fa5..80722cb3d6 100644 --- a/tools/subtype-gen/testdata/not_with_multiple_levels_test.golden.go +++ b/tools/subtype-gen/testdata/not_with_multiple_levels_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/oneof_expression_test.golden.go b/tools/subtype-gen/testdata/oneof_expression_test.golden.go index 65b1e5f7be..7685f3b406 100644 --- a/tools/subtype-gen/testdata/oneof_expression_test.golden.go +++ b/tools/subtype-gen/testdata/oneof_expression_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/oneof_with_not_test.golden.go b/tools/subtype-gen/testdata/oneof_with_not_test.golden.go index 104dd22fa1..0b5f820ab8 100644 --- a/tools/subtype-gen/testdata/oneof_with_not_test.golden.go +++ b/tools/subtype-gen/testdata/oneof_with_not_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/or_predicate_test.golden.go b/tools/subtype-gen/testdata/or_predicate_test.golden.go index 725a158e24..483b62e81a 100644 --- a/tools/subtype-gen/testdata/or_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/or_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/permits_predicate_test.golden.go b/tools/subtype-gen/testdata/permits_predicate_test.golden.go index 01b0df75f4..7f4176ef72 100644 --- a/tools/subtype-gen/testdata/permits_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/permits_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/return_covariant_predicate_test.golden.go b/tools/subtype-gen/testdata/return_covariant_predicate_test.golden.go index f3a5d26423..25f6d43f01 100644 --- a/tools/subtype-gen/testdata/return_covariant_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/return_covariant_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/set_contains_predicate_test.golden.go b/tools/subtype-gen/testdata/set_contains_predicate_test.golden.go index 32790706a1..245fe9a8e8 100644 --- a/tools/subtype-gen/testdata/set_contains_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/set_contains_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/simple_and_complex_type_test.golden.go b/tools/subtype-gen/testdata/simple_and_complex_type_test.golden.go index 7f21f25e07..da6772d085 100644 --- a/tools/subtype-gen/testdata/simple_and_complex_type_test.golden.go +++ b/tools/subtype-gen/testdata/simple_and_complex_type_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/simple_type_test.golden.go b/tools/subtype-gen/testdata/simple_type_test.golden.go index 9ba9dcd67a..cd92db53d4 100644 --- a/tools/subtype-gen/testdata/simple_type_test.golden.go +++ b/tools/subtype-gen/testdata/simple_type_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } diff --git a/tools/subtype-gen/testdata/subtype_predicate_test.golden.go b/tools/subtype-gen/testdata/subtype_predicate_test.golden.go index c2532cce33..f26bd576e5 100644 --- a/tools/subtype-gen/testdata/subtype_predicate_test.golden.go +++ b/tools/subtype-gen/testdata/subtype_predicate_test.golden.go @@ -19,7 +19,7 @@ package sema -func checkSubTypeWithoutEquality_gen(subType Type, superType Type) bool { +func CheckSubTypeWithoutEquality_gen(subType Type, superType Type) bool { if subType == NeverType { return true } From 4fe1ea6ec1b797b48bec09109881c75cd8039549 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 14 Nov 2025 09:16:38 -0800 Subject: [PATCH 6/7] Fix lint --- runtime/subtype_check_test.go | 4 ++-- sema/type.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/subtype_check_test.go b/runtime/subtype_check_test.go index 3510a770e7..5f31561601 100644 --- a/runtime/subtype_check_test.go +++ b/runtime/subtype_check_test.go @@ -21,12 +21,12 @@ package runtime import ( "testing" - "github.com/onflow/cadence/interpreter" - "github.com/onflow/cadence/sema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" + "github.com/onflow/cadence/sema" ) var location = common.NewStringLocation(nil, "test") diff --git a/sema/type.go b/sema/type.go index 0bf896563e..93b277fa33 100644 --- a/sema/type.go +++ b/sema/type.go @@ -7882,8 +7882,8 @@ func CheckSubTypeWithoutEquality(subType Type, superType Type) bool { // `Us` and `Ws` do *not* have to be subsets: // The owner may freely restrict and unrestrict. - return intersectionSubType.Equal(intersectionSuperType) - } + return intersectionSubType.Equal(intersectionSuperType) + } case *CompositeType: // A type `T` From 0c933655e25442d01e09eb04dc0cb44fb367c3fb Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 14 Nov 2025 12:26:17 -0800 Subject: [PATCH 7/7] Add exporting support for storable static type --- encoding/ccf/ccf_test.go | 1 + encoding/ccf/simpletype.go | 3 +++ encoding/ccf/simpletype_string.go | 11 +++++++---- encoding/ccf/simpletype_test.go | 4 +--- runtime/convertTypes.go | 2 ++ types.go | 2 +- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 4d2eaa14a7..d07b72f379 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -8783,6 +8783,7 @@ func TestEncodeSimpleTypes(t *testing.T) { ccf.SimpleTypeIssueAccountCapabilityController: cadence.IssueAccountCapabilityControllerType, ccf.SimpleTypeCapabilitiesMapping: cadence.CapabilitiesMappingType, ccf.SimpleTypeAccountMapping: cadence.AccountMappingType, + ccf.SimpleTypeStorable: cadence.StorableType, } var missingTests []string diff --git a/encoding/ccf/simpletype.go b/encoding/ccf/simpletype.go index 48bbba6d84..4dc68d24d3 100644 --- a/encoding/ccf/simpletype.go +++ b/encoding/ccf/simpletype.go @@ -150,6 +150,8 @@ const ( // Cadence simple type IDs SimpleTypeUFix128 _ + SimpleTypeStorable + // !!! *WARNING* !!! // ADD NEW TYPES *BEFORE* THIS WARNING. // DO *NOT* ADD NEW TYPES AFTER THIS LINE! @@ -174,6 +176,7 @@ func initSimpleTypeIDBiMap() (m *bimap.BiMap[cadence.PrimitiveType, SimpleType]) m.Insert(cadence.StringType, SimpleTypeString) m.Insert(cadence.CharacterType, SimpleTypeCharacter) m.Insert(cadence.HashableStructType, SimpleTypeHashableStruct) + m.Insert(cadence.StorableType, SimpleTypeStorable) m.Insert(cadence.NumberType, SimpleTypeNumber) m.Insert(cadence.SignedNumberType, SimpleTypeSignedNumber) diff --git a/encoding/ccf/simpletype_string.go b/encoding/ccf/simpletype_string.go index 49cb3087df..83a67907bf 100644 --- a/encoding/ccf/simpletype_string.go +++ b/encoding/ccf/simpletype_string.go @@ -102,7 +102,8 @@ func _() { _ = x[SimpleTypeFixedSizeUnsignedInteger-98] _ = x[SimpleTypeFix128-99] _ = x[SimpleTypeUFix128-101] - _ = x[SimpleType_Count-103] + _ = x[SimpleTypeStorable-103] + _ = x[SimpleType_Count-104] } const ( @@ -110,12 +111,13 @@ const ( _SimpleType_name_1 = "SimpleTypeDeployedContract" _SimpleType_name_2 = "SimpleTypeBlockSimpleTypeAnySimpleTypeAnyStructSimpleTypeAnyResourceSimpleTypeMetaTypeSimpleTypeNeverSimpleTypeNumberSimpleTypeSignedNumberSimpleTypeIntegerSimpleTypeSignedIntegerSimpleTypeFixedPointSimpleTypeSignedFixedPointSimpleTypeBytesSimpleTypeVoidSimpleTypeFunctionSimpleTypeWord128SimpleTypeWord256SimpleTypeAnyStructAttachmentTypeSimpleTypeAnyResourceAttachmentTypeSimpleTypeStorageCapabilityControllerSimpleTypeAccountCapabilityControllerSimpleTypeAccountSimpleTypeAccount_ContractsSimpleTypeAccount_KeysSimpleTypeAccount_InboxSimpleTypeAccount_StorageCapabilitiesSimpleTypeAccount_AccountCapabilitiesSimpleTypeAccount_CapabilitiesSimpleTypeAccount_StorageSimpleTypeMutateSimpleTypeInsertSimpleTypeRemoveSimpleTypeIdentitySimpleTypeStorageSimpleTypeSaveValueSimpleTypeLoadValueSimpleTypeCopyValueSimpleTypeBorrowValueSimpleTypeContractsSimpleTypeAddContractSimpleTypeUpdateContractSimpleTypeRemoveContractSimpleTypeKeysSimpleTypeAddKeySimpleTypeRevokeKeySimpleTypeInboxSimpleTypePublishInboxCapabilitySimpleTypeUnpublishInboxCapabilitySimpleTypeClaimInboxCapabilitySimpleTypeCapabilitiesSimpleTypeStorageCapabilitiesSimpleTypeAccountCapabilitiesSimpleTypePublishCapabilitySimpleTypeUnpublishCapabilitySimpleTypeGetStorageCapabilityControllerSimpleTypeIssueStorageCapabilityControllerSimpleTypeGetAccountCapabilityControllerSimpleTypeIssueAccountCapabilityControllerSimpleTypeCapabilitiesMappingSimpleTypeAccountMappingSimpleTypeHashableStructSimpleTypeFixedSizeUnsignedIntegerSimpleTypeFix128" _SimpleType_name_3 = "SimpleTypeUFix128" - _SimpleType_name_4 = "SimpleType_Count" + _SimpleType_name_4 = "SimpleTypeStorableSimpleType_Count" ) var ( _SimpleType_index_0 = [...]uint16{0, 14, 30, 49, 66, 79, 93, 108, 123, 138, 154, 170, 184, 199, 215, 231, 247, 264, 281, 296, 312, 328, 344, 359, 375, 389, 413, 434, 454, 475} _SimpleType_index_2 = [...]uint16{0, 15, 28, 47, 68, 86, 101, 117, 139, 156, 179, 199, 225, 240, 254, 272, 289, 306, 339, 374, 411, 448, 465, 492, 514, 537, 574, 611, 641, 666, 682, 698, 714, 732, 749, 768, 787, 806, 827, 846, 867, 891, 915, 929, 945, 964, 979, 1011, 1045, 1075, 1097, 1126, 1155, 1182, 1211, 1251, 1293, 1333, 1375, 1404, 1428, 1452, 1486, 1502} + _SimpleType_index_4 = [...]uint8{0, 18, 34} ) func (i SimpleType) String() string { @@ -129,8 +131,9 @@ func (i SimpleType) String() string { return _SimpleType_name_2[_SimpleType_index_2[i]:_SimpleType_index_2[i+1]] case i == 101: return _SimpleType_name_3 - case i == 103: - return _SimpleType_name_4 + case 103 <= i && i <= 104: + i -= 103 + return _SimpleType_name_4[_SimpleType_index_4[i]:_SimpleType_index_4[i+1]] default: return "SimpleType(" + strconv.FormatInt(int64(i), 10) + ")" } diff --git a/encoding/ccf/simpletype_test.go b/encoding/ccf/simpletype_test.go index f0a25dd075..528df7d2c7 100644 --- a/encoding/ccf/simpletype_test.go +++ b/encoding/ccf/simpletype_test.go @@ -52,9 +52,7 @@ func TestTypeConversion(t *testing.T) { } for ty := interpreter.PrimitiveStaticType(1); ty < interpreter.PrimitiveStaticType_Count; ty++ { - if !ty.IsDefined() || - ty.IsDeprecated() || //nolint:staticcheck - ty == interpreter.PrimitiveStaticTypeStorable { + if !ty.IsDefined() || ty.IsDeprecated() { //nolint:staticcheck continue } diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index 52b7f48d1f..914ee5ea1d 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -172,6 +172,8 @@ func ExportMeteredType( return cadence.AccountType case sema.DeployedContractType: return cadence.DeployedContractType + case sema.StorableType: + return cadence.StorableType case sema.MutateType: return cadence.MutateType diff --git a/types.go b/types.go index cae848f9ca..816ed9d470 100644 --- a/types.go +++ b/types.go @@ -125,13 +125,13 @@ var AnyResourceType = PrimitiveType(interpreter.PrimitiveStaticTypeAnyResource) var AnyStructAttachmentType = PrimitiveType(interpreter.PrimitiveStaticTypeAnyStructAttachment) var AnyResourceAttachmentType = PrimitiveType(interpreter.PrimitiveStaticTypeAnyResourceAttachment) var HashableStructType = PrimitiveType(interpreter.PrimitiveStaticTypeHashableStruct) - var BoolType = PrimitiveType(interpreter.PrimitiveStaticTypeBool) var AddressType = PrimitiveType(interpreter.PrimitiveStaticTypeAddress) var StringType = PrimitiveType(interpreter.PrimitiveStaticTypeString) var CharacterType = PrimitiveType(interpreter.PrimitiveStaticTypeCharacter) var MetaType = PrimitiveType(interpreter.PrimitiveStaticTypeMetaType) var BlockType = PrimitiveType(interpreter.PrimitiveStaticTypeBlock) +var StorableType = PrimitiveType(interpreter.PrimitiveStaticTypeStorable) var NumberType = PrimitiveType(interpreter.PrimitiveStaticTypeNumber) var SignedNumberType = PrimitiveType(interpreter.PrimitiveStaticTypeSignedNumber)