From f384c22dcdafa7c04809051d249495f5b8564d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 29 Jan 2024 16:36:23 -0800 Subject: [PATCH 01/15] extend type bounds --- runtime/convertTypes.go | 7 +- runtime/interpreter/div_mod_test.go | 4 +- runtime/interpreter/value_test.go | 24 +- runtime/literal_test.go | 4 +- runtime/runtime_test.go | 22 +- runtime/sema/account.gen.go | 72 ++-- runtime/sema/check_binary_expression.go | 2 +- runtime/sema/checker.go | 13 +- runtime/sema/errors.go | 29 ++ runtime/sema/gen/main.go | 15 +- .../gen/testdata/functions/test.golden.go | 8 +- runtime/sema/type.go | 79 ++-- runtime/sema/type_test.go | 32 +- runtime/sema/typebound.go | 382 ++++++++++++++++++ runtime/sema/typebound_test.go | 165 ++++++++ runtime/stdlib/account.go | 4 +- runtime/stdlib/random.go | 4 +- runtime/stdlib/random_test.go | 2 +- runtime/stdlib/range.go | 4 +- runtime/stdlib/test_contract.go | 24 +- runtime/stdlib/test_test.go | 20 +- runtime/tests/checker/account_test.go | 20 +- .../tests/checker/builtinfunctions_test.go | 16 +- runtime/tests/checker/capability_test.go | 8 +- runtime/tests/checker/entitlements_test.go | 4 +- runtime/tests/checker/fixedpoint_test.go | 28 +- runtime/tests/checker/for_test.go | 10 +- runtime/tests/checker/genericfunction_test.go | 36 +- runtime/tests/checker/integer_test.go | 18 +- runtime/tests/checker/operations_test.go | 8 +- runtime/tests/checker/range_value_test.go | 9 +- runtime/tests/checker/typeargument_test.go | 14 +- runtime/tests/interpreter/arithmetic_test.go | 8 +- runtime/tests/interpreter/fixedpoint_test.go | 27 +- runtime/tests/interpreter/for_test.go | 16 +- runtime/tests/interpreter/integers_test.go | 26 +- runtime/tests/interpreter/interpreter_test.go | 7 +- runtime/tests/interpreter/range_value_test.go | 23 +- runtime/tests/interpreter/reference_test.go | 28 +- 39 files changed, 874 insertions(+), 348 deletions(-) create mode 100644 runtime/sema/typebound.go create mode 100644 runtime/sema/typebound_test.go diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index 441eea8b07..d84a781168 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -516,7 +516,12 @@ func exportFunctionType( typeBound := typeParameter.TypeBound var convertedParameterTypeBound cadence.Type if typeBound != nil { - convertedParameterTypeBound = ExportMeteredType(gauge, typeBound, results) + convertedParameterTypeBound = ExportMeteredType( + gauge, + // TODO: + typeBound.(sema.SubtypeTypeBound).Type, + results, + ) } // Metered above diff --git a/runtime/interpreter/div_mod_test.go b/runtime/interpreter/div_mod_test.go index eeb2ca6d38..8b5354d5cd 100644 --- a/runtime/interpreter/div_mod_test.go +++ b/runtime/interpreter/div_mod_test.go @@ -3388,7 +3388,7 @@ func TestNegativeMod(t *testing.T) { }, } - for _, integerType := range sema.AllSignedIntegerTypes { + for _, integerType := range sema.AllLeafSignedIntegerTypes { if _, ok := tests[integerType.String()]; !ok { panic(fmt.Sprintf("broken test: missing %s", integerType)) } @@ -3414,7 +3414,7 @@ func TestNegativeMod(t *testing.T) { }, } - for _, integerType := range sema.AllSignedFixedPointTypes { + for _, integerType := range sema.AllLeafSignedFixedPointTypes { if _, ok := tests[integerType.String()]; !ok { panic(fmt.Sprintf("broken test: missing %s", integerType)) } diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index ad5431c5a6..e98b89e18c 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -3489,13 +3489,7 @@ func TestNumberValueIntegerConversion(t *testing.T) { sema.Int256Type: NewUnmeteredInt256ValueFromInt64(42), } - for _, ty := range sema.AllIntegerTypes { - // Only test leaf types - switch ty { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } - + for _, ty := range sema.AllLeafIntegerTypes { _, ok := testValues[ty.(*sema.NumericType)] require.True(t, ok, "missing expected value for type %s", ty.String()) } @@ -3794,13 +3788,7 @@ func TestValue_ConformsToStaticType(t *testing.T) { sema.Int256Type: NewUnmeteredInt256ValueFromInt64(42), } - for _, ty := range sema.AllIntegerTypes { - // Only test leaf types - switch ty { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } - + for _, ty := range sema.AllLeafIntegerTypes { _, ok := testCases[ty.(*sema.NumericType)] require.True(t, ok, "missing case for type %s", ty.String()) } @@ -3826,13 +3814,7 @@ func TestValue_ConformsToStaticType(t *testing.T) { sema.Fix64Type: NewUnmeteredFix64ValueWithInteger(42, EmptyLocationRange), } - for _, ty := range sema.AllFixedPointTypes { - // Only test leaf types - switch ty { - case sema.FixedPointType, sema.SignedFixedPointType: - continue - } - + for _, ty := range sema.AllLeafFixedPointTypes { _, ok := testCases[ty.(*sema.FixedPointNumericType)] require.True(t, ok, "missing case for type %s", ty.String()) } diff --git a/runtime/literal_test.go b/runtime/literal_test.go index 1fe6450373..7a8cf3b349 100644 --- a/runtime/literal_test.go +++ b/runtime/literal_test.go @@ -607,7 +607,7 @@ func TestRuntimeParseLiteral(t *testing.T) { require.Nil(t, value) }) - for _, unsignedIntegerType := range sema.AllUnsignedIntegerTypes { + for _, unsignedIntegerType := range sema.AllLeafUnsignedIntegerTypes { t.Run( fmt.Sprintf( @@ -649,7 +649,7 @@ func TestRuntimeParseLiteral(t *testing.T) { } for _, signedIntegerType := range common.Concat( - sema.AllSignedIntegerTypes, + sema.AllLeafSignedIntegerTypes, []sema.Type{ sema.IntegerType, sema.SignedIntegerType, diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 648c6eb96c..fb5e7a14d8 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -1733,10 +1733,24 @@ func TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction(t *testing.T) errs := checker.RequireCheckerErrors(t, checkerErr, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + var typeBoundError *sema.TypeBoundError + require.ErrorAs(t, errs[0], &typeBoundError) + + assert.Equal( + t, + sema.SubtypeTypeBound{ + Type: sema.StorableType, + }, + typeBoundError.ExpectedTypeBound, + ) + assert.Equal( + t, + &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, + typeBoundError.ActualType, + ) - typeMismatchError := errs[0].(*sema.TypeMismatchError) - assert.Contains(t, typeMismatchError.SecondaryError(), "expected `Storable`, got `InclusiveRange`") } func TestRuntimeResourceContractUseThroughReference(t *testing.T) { @@ -4581,7 +4595,7 @@ func TestRuntimeRandom(t *testing.T) { } testTypes := func(t *testing.T, testType func(*testing.T, sema.Type)) { - for _, ty := range sema.AllFixedSizeUnsignedIntegerTypes { + for _, ty := range sema.AllLeafFixedSizeUnsignedIntegerTypes { ty := ty t.Run(ty.String(), func(t *testing.T) { t.Parallel() diff --git a/runtime/sema/account.gen.go b/runtime/sema/account.gen.go index 1e1fbbc8d3..1431a9cae3 100644 --- a/runtime/sema/account.gen.go +++ b/runtime/sema/account.gen.go @@ -128,7 +128,7 @@ const Account_StorageTypeSaveFunctionName = "save" var Account_StorageTypeSaveFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: StorableType, + TypeBound: SubtypeTypeBound{Type: StorableType}, } var Account_StorageTypeSaveFunctionType = &FunctionType{ @@ -194,7 +194,7 @@ const Account_StorageTypeLoadFunctionName = "load" var Account_StorageTypeLoadFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: StorableType, + TypeBound: SubtypeTypeBound{Type: StorableType}, } var Account_StorageTypeLoadFunctionType = &FunctionType{ @@ -237,7 +237,7 @@ const Account_StorageTypeCopyFunctionName = "copy" var Account_StorageTypeCopyFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: AnyStructType, + TypeBound: SubtypeTypeBound{Type: AnyStructType}, } var Account_StorageTypeCopyFunctionType = &FunctionType{ @@ -280,7 +280,7 @@ const Account_StorageTypeCheckFunctionName = "check" var Account_StorageTypeCheckFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: AnyType, + TypeBound: SubtypeTypeBound{Type: AnyType}, } var Account_StorageTypeCheckFunctionType = &FunctionType{ @@ -312,9 +312,11 @@ const Account_StorageTypeBorrowFunctionName = "borrow" var Account_StorageTypeBorrowFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - Authorization: UnauthorizedAccess, + TypeBound: SubtypeTypeBound{ + Type: &ReferenceType{ + Type: AnyType, + Authorization: UnauthorizedAccess, + }, }, } @@ -735,9 +737,11 @@ const Account_ContractsTypeBorrowFunctionName = "borrow" var Account_ContractsTypeBorrowFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - Authorization: UnauthorizedAccess, + TypeBound: SubtypeTypeBound{ + Type: &ReferenceType{ + Type: AnyType, + Authorization: UnauthorizedAccess, + }, }, } @@ -1060,9 +1064,11 @@ const Account_InboxTypeUnpublishFunctionName = "unpublish" var Account_InboxTypeUnpublishFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - Authorization: UnauthorizedAccess, + TypeBound: SubtypeTypeBound{ + Type: &ReferenceType{ + Type: AnyType, + Authorization: UnauthorizedAccess, + }, }, } @@ -1101,9 +1107,11 @@ const Account_InboxTypeClaimFunctionName = "claim" var Account_InboxTypeClaimFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - Authorization: UnauthorizedAccess, + TypeBound: SubtypeTypeBound{ + Type: &ReferenceType{ + Type: AnyType, + Authorization: UnauthorizedAccess, + }, }, } @@ -1214,9 +1222,11 @@ const Account_CapabilitiesTypeGetFunctionName = "get" var Account_CapabilitiesTypeGetFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - Authorization: UnauthorizedAccess, + TypeBound: SubtypeTypeBound{ + Type: &ReferenceType{ + Type: AnyType, + Authorization: UnauthorizedAccess, + }, }, } @@ -1254,9 +1264,11 @@ const Account_CapabilitiesTypeBorrowFunctionName = "borrow" var Account_CapabilitiesTypeBorrowFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - Authorization: UnauthorizedAccess, + TypeBound: SubtypeTypeBound{ + Type: &ReferenceType{ + Type: AnyType, + Authorization: UnauthorizedAccess, + }, }, } @@ -1446,9 +1458,11 @@ const Account_StorageCapabilitiesTypeIssueFunctionName = "issue" var Account_StorageCapabilitiesTypeIssueFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - Authorization: UnauthorizedAccess, + TypeBound: SubtypeTypeBound{ + Type: &ReferenceType{ + Type: AnyType, + Authorization: UnauthorizedAccess, + }, }, } @@ -1669,9 +1683,11 @@ const Account_AccountCapabilitiesTypeIssueFunctionName = "issue" var Account_AccountCapabilitiesTypeIssueFunctionTypeParameterT = &TypeParameter{ Name: "T", - TypeBound: &ReferenceType{ - Type: AccountType, - Authorization: UnauthorizedAccess, + TypeBound: SubtypeTypeBound{ + Type: &ReferenceType{ + Type: AccountType, + Authorization: UnauthorizedAccess, + }, }, } diff --git a/runtime/sema/check_binary_expression.go b/runtime/sema/check_binary_expression.go index b4f77fbc2d..6155ad8f63 100644 --- a/runtime/sema/check_binary_expression.go +++ b/runtime/sema/check_binary_expression.go @@ -118,7 +118,7 @@ func (checker *Checker) VisitBinaryExpression(expression *ast.BinaryExpression) // ``` if expectedType == nil || - (leftType != expectedType && IsProperSubType(leftType, expectedType)) { + (leftType != expectedType && IsStrictSubType(leftType, expectedType)) { expectedType = leftType } diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 00344e250f..c3946f8f34 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -41,8 +41,10 @@ const ResultIdentifier = "result" var beforeType = func() *FunctionType { typeParameter := &TypeParameter{ - Name: "T", - TypeBound: AnyStructType, + Name: "T", + TypeBound: SubtypeTypeBound{ + Type: AnyStructType, + }, } typeAnnotation := NewTypeAnnotation( @@ -1370,11 +1372,14 @@ func (checker *Checker) typeParameters(typeParameterList *ast.TypeParameterList) for i, typeParameter := range typeParameterList.TypeParameters { typeBoundAnnotation := typeParameter.TypeBound - var convertedTypeBound Type + var convertedTypeBound TypeBound if typeBoundAnnotation != nil { + // TODO: for now, the syntax only supports subtype type bounds convertedTypeBoundAnnotation := checker.ConvertTypeAnnotation(typeBoundAnnotation) checker.checkTypeAnnotation(convertedTypeBoundAnnotation, typeBoundAnnotation) - convertedTypeBound = convertedTypeBoundAnnotation.Type + convertedTypeBound = SubtypeTypeBound{ + Type: convertedTypeBoundAnnotation.Type, + } } typeParameters[i] = &TypeParameter{ diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 9b289350c0..c41f1b054c 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -333,6 +333,35 @@ func (e *TypeMismatchWithDescriptionError) SecondaryError() string { ) } +// TypeBoundError + +type TypeBoundError struct { + ExpectedTypeBound TypeBound + ActualType Type + Expression ast.Expression + ast.Range +} + +var _ SemanticError = &TypeBoundError{} +var _ errors.UserError = &TypeBoundError{} +var _ errors.SecondaryError = &TypeBoundError{} + +func (*TypeBoundError) isSemanticError() {} + +func (*TypeBoundError) IsUserError() {} + +func (e *TypeBoundError) Error() string { + return "type bound unsatisfied" +} + +func (e *TypeBoundError) SecondaryError() string { + return fmt.Sprintf( + "expected type satisfying %s, got `%s`", + e.ExpectedTypeBound, + e.ActualType.QualifiedString(), + ) +} + // NotIndexableTypeError type NotIndexableTypeError struct { diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index b69133a6f2..c7ab6036b2 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -2086,9 +2086,22 @@ func typeParameterExpr(name string, typeBound dst.Expr) dst.Expr { goKeyValue("Name", goStringLit(name)), } if typeBound != nil { + subtypeTypeBoundLit := &dst.CompositeLit{ + Type: &dst.Ident{ + Name: "SubtypeTypeBound", + Path: semaPath, + }, + Elts: []dst.Expr{ + goKeyValue("Type", typeBound), + }, + } + elements = append( elements, - goKeyValue("TypeBound", typeBound), + goKeyValue( + "TypeBound", + subtypeTypeBoundLit, + ), ) } diff --git a/runtime/sema/gen/testdata/functions/test.golden.go b/runtime/sema/gen/testdata/functions/test.golden.go index 8a1baee350..b0d593224d 100644 --- a/runtime/sema/gen/testdata/functions/test.golden.go +++ b/runtime/sema/gen/testdata/functions/test.golden.go @@ -117,9 +117,11 @@ const TestTypeTypeParamWithBoundFunctionName = "typeParamWithBound" var TestTypeTypeParamWithBoundFunctionTypeParameterT = &sema.TypeParameter{ Name: "T", - TypeBound: &sema.ReferenceType{ - Type: sema.AnyType, - Authorization: sema.UnauthorizedAccess, + TypeBound: sema.SubtypeTypeBound{ + Type: &sema.ReferenceType{ + Type: sema.AnyType, + Authorization: sema.UnauthorizedAccess, + }, }, } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index fb1c21ccc7..ede117cc01 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3310,7 +3310,7 @@ func (p Parameter) EffectiveArgumentLabel() string { // TypeParameter type TypeParameter struct { - TypeBound Type + TypeBound TypeBound Name string Optional bool } @@ -3320,7 +3320,8 @@ func (p TypeParameter) string(typeFormatter func(Type) string) string { builder.WriteString(p.Name) if p.TypeBound != nil { builder.WriteString(": ") - builder.WriteString(typeFormatter(p.TypeBound)) + // TODO: + builder.WriteString(typeFormatter(p.TypeBound.(SubtypeTypeBound).Type)) } return builder.String() } @@ -3359,17 +3360,17 @@ func (p TypeParameter) Equal(other *TypeParameter) bool { func (p TypeParameter) checkTypeBound(ty Type, memoryGauge common.MemoryGauge, typeRange ast.HasPosition) error { if p.TypeBound == nil || - p.TypeBound.IsInvalidType() || + p.TypeBound.HasInvalidType() || ty.IsInvalidType() { return nil } - if !IsSubType(ty, p.TypeBound) { - return &TypeMismatchError{ - ExpectedType: p.TypeBound, - ActualType: ty, - Range: ast.NewRangeFromPositioned(memoryGauge, typeRange), + if !p.TypeBound.Satisfies(ty) { + return &TypeBoundError{ + ExpectedTypeBound: p.TypeBound, + ActualType: ty, + Range: ast.NewRangeFromPositioned(memoryGauge, typeRange), } } @@ -3710,7 +3711,7 @@ func (t *FunctionType) IsInvalidType() bool { for _, typeParameter := range t.TypeParameters { if typeParameter.TypeBound != nil && - typeParameter.TypeBound.IsInvalidType() { + typeParameter.TypeBound.HasInvalidType() { return true } @@ -3784,17 +3785,17 @@ func (t *FunctionType) TypeAnnotationState() TypeAnnotationState { func (t *FunctionType) RewriteWithIntersectionTypes() (Type, bool) { anyRewritten := false - rewrittenTypeParameterTypeBounds := map[*TypeParameter]Type{} + rewrittenTypeParameterTypeBounds := map[*TypeParameter]TypeBound{} for _, typeParameter := range t.TypeParameters { if typeParameter.TypeBound == nil { continue } - rewrittenType, rewritten := typeParameter.TypeBound.RewriteWithIntersectionTypes() + rewrittenTypeBound, rewritten := typeParameter.TypeBound.RewriteWithIntersectionTypes() if rewritten { anyRewritten = true - rewrittenTypeParameterTypeBounds[typeParameter] = rewrittenType + rewrittenTypeParameterTypeBounds[typeParameter] = rewrittenTypeBound } } @@ -4170,24 +4171,30 @@ func baseTypeVariable(name string, ty Type) *Variable { // the values available in programs var BaseValueActivation = NewVariableActivation(nil) -var AllSignedFixedPointTypes = []Type{ +var AllLeafSignedFixedPointTypes = []Type{ Fix64Type, } -var AllUnsignedFixedPointTypes = []Type{ +var AllLeafUnsignedFixedPointTypes = []Type{ UFix64Type, } +var AllLeafFixedPointTypes = common.Concat( + AllLeafUnsignedFixedPointTypes, + AllLeafSignedFixedPointTypes, +) + +var AllNonLeafFixedPointTypes = []Type{ + FixedPointType, + SignedFixedPointType, +} + var AllFixedPointTypes = common.Concat( - AllUnsignedFixedPointTypes, - AllSignedFixedPointTypes, - []Type{ - FixedPointType, - SignedFixedPointType, - }, + AllLeafFixedPointTypes, + AllNonLeafFixedPointTypes, ) -var AllSignedIntegerTypes = []Type{ +var AllLeafSignedIntegerTypes = []Type{ IntType, Int8Type, Int16Type, @@ -4197,7 +4204,7 @@ var AllSignedIntegerTypes = []Type{ Int256Type, } -var AllFixedSizeUnsignedIntegerTypes = []Type{ +var AllLeafFixedSizeUnsignedIntegerTypes = []Type{ // UInt* UInt8Type, UInt16Type, @@ -4214,8 +4221,8 @@ var AllFixedSizeUnsignedIntegerTypes = []Type{ Word256Type, } -var AllUnsignedIntegerTypes = common.Concat( - AllFixedSizeUnsignedIntegerTypes, +var AllLeafUnsignedIntegerTypes = common.Concat( + AllLeafFixedSizeUnsignedIntegerTypes, []Type{ UIntType, }, @@ -4227,9 +4234,13 @@ var AllNonLeafIntegerTypes = []Type{ FixedSizeUnsignedIntegerType, } +var AllLeafIntegerTypes = common.Concat( + AllLeafUnsignedIntegerTypes, + AllLeafSignedIntegerTypes, +) + var AllIntegerTypes = common.Concat( - AllUnsignedIntegerTypes, - AllSignedIntegerTypes, + AllLeafIntegerTypes, AllNonLeafIntegerTypes, ) @@ -6601,7 +6612,7 @@ func (t *InclusiveRangeType) CheckInstantiated(pos ast.HasPosition, memoryGauge var inclusiveRangeTypeParameter = &TypeParameter{ Name: "T", - TypeBound: IntegerType, + TypeBound: SubtypeTypeBound{Type: IntegerType}, } func (*InclusiveRangeType) TypeParameters() []*TypeParameter { @@ -7270,11 +7281,11 @@ func IsSameTypeKind(subType Type, superType Type) bool { return IsSubType(subType, superType) } -// IsProperSubType is similar to IsSubType, +// IsStrictSubType is similar to IsSubType, // i.e. it determines if the given subtype is a subtype // of the given supertype, but returns false // if the subtype and supertype refer to the same type. -func IsProperSubType(subType Type, superType Type) bool { +func IsStrictSubType(subType Type, superType Type) bool { if subType.Equal(superType) { return false @@ -7288,7 +7299,7 @@ func IsProperSubType(subType Type, superType Type) bool { // 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 +// Consider using IsSubType or IsStrictSubType func checkSubTypeWithoutEquality(subType Type, superType Type) bool { if subType == NeverType { @@ -8343,9 +8354,11 @@ func (t *CapabilityType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Typ var capabilityTypeParameter = &TypeParameter{ Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - Authorization: UnauthorizedAccess, + TypeBound: SubtypeTypeBound{ + Type: &ReferenceType{ + Type: AnyType, + Authorization: UnauthorizedAccess, + }, }, } diff --git a/runtime/sema/type_test.go b/runtime/sema/type_test.go index f96c6420f4..21c0d5b05b 100644 --- a/runtime/sema/type_test.go +++ b/runtime/sema/type_test.go @@ -2066,7 +2066,7 @@ func TestTypeInclusions(t *testing.T) { t.Run("SignedInteger", func(t *testing.T) { t.Parallel() - for _, typ := range AllSignedIntegerTypes { + for _, typ := range AllLeafSignedIntegerTypes { t.Run(typ.String(), func(t *testing.T) { assert.True(t, SignedIntegerTypeTag.ContainsAny(typ.Tag())) }) @@ -2076,7 +2076,7 @@ func TestTypeInclusions(t *testing.T) { t.Run("UnsignedInteger", func(t *testing.T) { t.Parallel() - for _, typ := range AllUnsignedIntegerTypes { + for _, typ := range AllLeafUnsignedIntegerTypes { t.Run(typ.String(), func(t *testing.T) { assert.True(t, UnsignedIntegerTypeTag.ContainsAny(typ.Tag())) }) @@ -2086,7 +2086,7 @@ func TestTypeInclusions(t *testing.T) { t.Run("FixedSizeUnsignedInteger", func(t *testing.T) { t.Parallel() - for _, typ := range AllFixedSizeUnsignedIntegerTypes { + for _, typ := range AllLeafFixedSizeUnsignedIntegerTypes { t.Run(typ.String(), func(t *testing.T) { assert.True(t, FixedSizeUnsignedIntegerTypeTag.ContainsAny(typ.Tag())) }) @@ -2106,7 +2106,7 @@ func TestTypeInclusions(t *testing.T) { t.Run("SignedFixedPoint", func(t *testing.T) { t.Parallel() - for _, typ := range AllSignedFixedPointTypes { + for _, typ := range AllLeafSignedFixedPointTypes { t.Run(typ.String(), func(t *testing.T) { assert.True(t, SignedFixedPointTypeTag.ContainsAny(typ.Tag())) }) @@ -2116,7 +2116,7 @@ func TestTypeInclusions(t *testing.T) { t.Run("UnsignedFixedPoint", func(t *testing.T) { t.Parallel() - for _, typ := range AllUnsignedFixedPointTypes { + for _, typ := range AllLeafUnsignedFixedPointTypes { t.Run(typ.String(), func(t *testing.T) { assert.True(t, UnsignedFixedPointTypeTag.ContainsAny(typ.Tag())) }) @@ -2330,10 +2330,13 @@ func TestMapType(t *testing.T) { t.Run("map function type", func(t *testing.T) { t.Parallel() + originalTypeParam := &TypeParameter{ - TypeBound: Int64Type, - Name: "X", - Optional: true, + TypeBound: SubtypeTypeBound{ + Type: Int64Type, + }, + Name: "X", + Optional: true, } original := NewSimpleFunctionType( FunctionPurityView, @@ -2358,9 +2361,11 @@ func TestMapType(t *testing.T) { original.TypeParameters = []*TypeParameter{originalTypeParam} mappedTypeParam := &TypeParameter{ - TypeBound: StringType, - Name: "X", - Optional: true, + TypeBound: SubtypeTypeBound{ + Type: StringType, + }, + Name: "X", + Optional: true, } mapped := NewSimpleFunctionType( FunctionPurityView, @@ -2392,7 +2397,10 @@ func TestMapType(t *testing.T) { require.Equal(t, mapped, outputFunction) require.IsType(t, &GenericType{}, outputFunction.Parameters[0].TypeAnnotation.Type) - require.True(t, outputFunction.Parameters[0].TypeAnnotation.Type.(*GenericType).TypeParameter == outputFunction.TypeParameters[0]) + require.Same(t, + outputFunction.Parameters[0].TypeAnnotation.Type.(*GenericType).TypeParameter, + outputFunction.TypeParameters[0], + ) }) } diff --git a/runtime/sema/typebound.go b/runtime/sema/typebound.go new file mode 100644 index 0000000000..58ea88243e --- /dev/null +++ b/runtime/sema/typebound.go @@ -0,0 +1,382 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +import ( + "github.com/onflow/cadence/runtime/ast" + "github.com/onflow/cadence/runtime/common" +) + +// TypeBound + +type TypeBound interface { + isTypeBound() + Satisfies(ty Type) bool + HasInvalidType() bool + Equal(TypeBound) bool + CheckInstantiated( + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), + ) + Map( + gauge common.MemoryGauge, + typeParamMap map[*TypeParameter]*TypeParameter, + f func(Type) Type, + ) TypeBound + TypeAnnotationState() TypeAnnotationState + RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) +} + +// SubtypeTypeBound + +type SubtypeTypeBound struct { + Type Type +} + +var _ TypeBound = SubtypeTypeBound{} + +func (SubtypeTypeBound) isTypeBound() {} + +func (b SubtypeTypeBound) Satisfies(ty Type) bool { + return IsSubType(ty, b.Type) +} + +func (b SubtypeTypeBound) HasInvalidType() bool { + return b.Type.IsInvalidType() +} + +func (b SubtypeTypeBound) Equal(bound TypeBound) bool { + other, ok := bound.(SubtypeTypeBound) + if !ok { + return false + } + return b.Type.Equal(other.Type) +} + +func (b SubtypeTypeBound) CheckInstantiated( + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), +) { + b.Type.CheckInstantiated(pos, memoryGauge, report) +} + +func (b SubtypeTypeBound) Map( + gauge common.MemoryGauge, + typeParamMap map[*TypeParameter]*TypeParameter, + f func(Type) Type, +) TypeBound { + return SubtypeTypeBound{ + Type: b.Type.Map(gauge, typeParamMap, f), + } +} + +func (b SubtypeTypeBound) TypeAnnotationState() TypeAnnotationState { + return b.Type.TypeAnnotationState() +} + +func (b SubtypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { + rewrittenType, rewritten := b.Type.RewriteWithIntersectionTypes() + if rewritten { + return SubtypeTypeBound{ + Type: rewrittenType, + }, true + } + return b, false +} + +// StrictSubtypeTypeBound + +type StrictSubtypeTypeBound struct { + Type Type +} + +var _ TypeBound = StrictSubtypeTypeBound{} + +func (StrictSubtypeTypeBound) isTypeBound() {} + +func (b StrictSubtypeTypeBound) Satisfies(ty Type) bool { + return IsStrictSubType(ty, b.Type) +} + +func (b StrictSubtypeTypeBound) HasInvalidType() bool { + return b.Type.IsInvalidType() +} + +func (b StrictSubtypeTypeBound) Equal(bound TypeBound) bool { + other, ok := bound.(StrictSubtypeTypeBound) + if !ok { + return false + } + return b.Type.Equal(other.Type) +} + +func (b StrictSubtypeTypeBound) CheckInstantiated( + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), +) { + b.Type.CheckInstantiated(pos, memoryGauge, report) +} + +func (b StrictSubtypeTypeBound) Map( + gauge common.MemoryGauge, + typeParamMap map[*TypeParameter]*TypeParameter, + f func(Type) Type, +) TypeBound { + return SubtypeTypeBound{ + Type: b.Type.Map(gauge, typeParamMap, f), + } +} + +func (b StrictSubtypeTypeBound) TypeAnnotationState() TypeAnnotationState { + return b.Type.TypeAnnotationState() +} + +func (b StrictSubtypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { + rewrittenType, rewritten := b.Type.RewriteWithIntersectionTypes() + if rewritten { + return StrictSubtypeTypeBound{ + Type: rewrittenType, + }, true + } + return b, false +} + +// ConjunctionTypeBound + +type ConjunctionTypeBound struct { + TypeBounds []TypeBound +} + +var _ TypeBound = ConjunctionTypeBound{} + +func (ConjunctionTypeBound) isTypeBound() {} + +func (b ConjunctionTypeBound) Satisfies(ty Type) bool { + for _, typeBound := range b.TypeBounds { + if !typeBound.Satisfies(ty) { + return false + } + } + return true +} + +func (b ConjunctionTypeBound) HasInvalidType() bool { + for _, typeBound := range b.TypeBounds { + if typeBound.HasInvalidType() { + return true + } + } + return false +} + +func (b ConjunctionTypeBound) Equal(bound TypeBound) bool { + other, ok := bound.(ConjunctionTypeBound) + if !ok { + return false + } + + if len(b.TypeBounds) != len(other.TypeBounds) { + return false + } + + for i, typeBound := range b.TypeBounds { + otherTypeBound := other.TypeBounds[i] + if !typeBound.Equal(otherTypeBound) { + return false + } + } + + return true +} + +func (b ConjunctionTypeBound) CheckInstantiated( + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), +) { + for _, typeBound := range b.TypeBounds { + typeBound.CheckInstantiated(pos, memoryGauge, report) + } +} + +func (b ConjunctionTypeBound) Map( + gauge common.MemoryGauge, + typeParamMap map[*TypeParameter]*TypeParameter, + f func(Type) Type, +) TypeBound { + newTypeBounds := make([]TypeBound, 0, len(b.TypeBounds)) + for _, typeBound := range b.TypeBounds { + newTypeBounds = append( + newTypeBounds, + typeBound.Map(gauge, typeParamMap, f), + ) + } + return ConjunctionTypeBound{ + TypeBounds: newTypeBounds, + } +} + +func (b ConjunctionTypeBound) TypeAnnotationState() TypeAnnotationState { + for _, typeBound := range b.TypeBounds { + state := typeBound.TypeAnnotationState() + if state != TypeAnnotationStateValid { + return state + } + } + return TypeAnnotationStateValid +} + +func (b ConjunctionTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { + rewrittenTypeBounds := make([]TypeBound, 0, len(b.TypeBounds)) + for _, typeBound := range b.TypeBounds { + rewrittenTypeBound, currentRewritten := typeBound.RewriteWithIntersectionTypes() + if currentRewritten { + rewritten = true + rewrittenTypeBounds = append(rewrittenTypeBounds, rewrittenTypeBound) + } else { + rewrittenTypeBounds = append(rewrittenTypeBounds, typeBound) + } + } + if rewritten { + return ConjunctionTypeBound{ + TypeBounds: rewrittenTypeBounds, + }, true + } else { + return b, false + } +} + +// SupertypeTypeBound + +type SupertypeTypeBound struct { + Type Type +} + +var _ TypeBound = SupertypeTypeBound{} + +func (SupertypeTypeBound) isTypeBound() {} + +func (b SupertypeTypeBound) Satisfies(ty Type) bool { + return IsSubType(b.Type, ty) +} + +func (b SupertypeTypeBound) HasInvalidType() bool { + return b.Type.IsInvalidType() +} + +func (b SupertypeTypeBound) Equal(bound TypeBound) bool { + other, ok := bound.(SupertypeTypeBound) + if !ok { + return false + } + return b.Type.Equal(other.Type) +} + +func (b SupertypeTypeBound) CheckInstantiated( + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), +) { + b.Type.CheckInstantiated(pos, memoryGauge, report) +} + +func (b SupertypeTypeBound) Map( + gauge common.MemoryGauge, + typeParamMap map[*TypeParameter]*TypeParameter, + f func(Type) Type, +) TypeBound { + return SupertypeTypeBound{ + Type: b.Type.Map(gauge, typeParamMap, f), + } +} + +func (b SupertypeTypeBound) TypeAnnotationState() TypeAnnotationState { + return b.Type.TypeAnnotationState() +} + +func (b SupertypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { + rewrittenType, rewritten := b.Type.RewriteWithIntersectionTypes() + if rewritten { + return SupertypeTypeBound{ + Type: rewrittenType, + }, true + } + return b, false +} + +// StrictSupertypeTypeBound + +type StrictSupertypeTypeBound struct { + Type Type +} + +var _ TypeBound = StrictSupertypeTypeBound{} + +func (StrictSupertypeTypeBound) isTypeBound() {} + +func (b StrictSupertypeTypeBound) Satisfies(ty Type) bool { + return IsStrictSubType(b.Type, ty) +} + +func (b StrictSupertypeTypeBound) HasInvalidType() bool { + return b.Type.IsInvalidType() +} + +func (b StrictSupertypeTypeBound) Equal(bound TypeBound) bool { + other, ok := bound.(StrictSupertypeTypeBound) + if !ok { + return false + } + return b.Type.Equal(other.Type) +} + +func (b StrictSupertypeTypeBound) CheckInstantiated( + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), +) { + b.Type.CheckInstantiated(pos, memoryGauge, report) +} + +func (b StrictSupertypeTypeBound) Map( + gauge common.MemoryGauge, + typeParamMap map[*TypeParameter]*TypeParameter, + f func(Type) Type, +) TypeBound { + return StrictSupertypeTypeBound{ + Type: b.Type.Map(gauge, typeParamMap, f), + } +} + +func (b StrictSupertypeTypeBound) TypeAnnotationState() TypeAnnotationState { + return b.Type.TypeAnnotationState() +} + +func (b StrictSupertypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { + rewrittenType, rewritten := b.Type.RewriteWithIntersectionTypes() + if rewritten { + return StrictSupertypeTypeBound{ + Type: rewrittenType, + }, true + } + return b, false +} diff --git a/runtime/sema/typebound_test.go b/runtime/sema/typebound_test.go new file mode 100644 index 0000000000..7dc0dcc577 --- /dev/null +++ b/runtime/sema/typebound_test.go @@ -0,0 +1,165 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTypeBound_Satisfies(t *testing.T) { + t.Parallel() + + t.Run("subtype", func(t *testing.T) { + + t.Parallel() + + typeBound := SubtypeTypeBound{Type: IntegerType} + + assert.True(t, typeBound.Satisfies(IntegerType)) + assert.True(t, typeBound.Satisfies(NeverType)) + + for _, integerType := range AllLeafIntegerTypes { + assert.True(t, typeBound.Satisfies(integerType)) + } + }) + + t.Run("strict subtype", func(t *testing.T) { + + t.Parallel() + + typeBound := StrictSubtypeTypeBound{Type: IntegerType} + + assert.False(t, typeBound.Satisfies(IntegerType)) + assert.True(t, typeBound.Satisfies(NeverType)) + + for _, integerType := range AllLeafIntegerTypes { + assert.Truef(t, typeBound.Satisfies(integerType), "%s should satisfy", integerType) + } + }) + + t.Run("supertype", func(t *testing.T) { + + t.Parallel() + + typeBound := SupertypeTypeBound{Type: NeverType} + + assert.True(t, typeBound.Satisfies(NeverType)) + assert.True(t, typeBound.Satisfies(IntegerType)) + + for _, integerType := range AllLeafIntegerTypes { + assert.True(t, typeBound.Satisfies(integerType)) + } + }) + + t.Run("strict supertype", func(t *testing.T) { + + t.Parallel() + + typeBound := StrictSupertypeTypeBound{Type: NeverType} + + assert.False(t, typeBound.Satisfies(NeverType)) + assert.True(t, typeBound.Satisfies(IntegerType)) + + for _, integerType := range AllLeafIntegerTypes { + assert.True(t, typeBound.Satisfies(integerType)) + } + }) + + t.Run("conjunction", func(t *testing.T) { + + t.Parallel() + + typeBound := ConjunctionTypeBound{ + TypeBounds: []TypeBound{ + StrictSupertypeTypeBound{ + Type: NeverType, + }, + StrictSubtypeTypeBound{ + Type: FixedSizeUnsignedIntegerType, + }, + }, + } + + assert.False(t, typeBound.Satisfies(FixedSizeUnsignedIntegerType)) + assert.False(t, typeBound.Satisfies(NeverType)) + + for _, integerType := range AllLeafFixedSizeUnsignedIntegerTypes { + assert.True(t, typeBound.Satisfies(integerType)) + } + }) +} + +func TestTypeBound_HasInvalid(t *testing.T) { + t.Parallel() + + t.Run("subtype", func(t *testing.T) { + + t.Parallel() + + assert.False(t, SubtypeTypeBound{Type: IntegerType}.HasInvalidType()) + assert.True(t, SubtypeTypeBound{Type: InvalidType}.HasInvalidType()) + }) + + t.Run("strict subtype", func(t *testing.T) { + + t.Parallel() + + assert.False(t, StrictSubtypeTypeBound{Type: IntegerType}.HasInvalidType()) + assert.True(t, StrictSubtypeTypeBound{Type: InvalidType}.HasInvalidType()) + }) + + t.Run("supertype", func(t *testing.T) { + + t.Parallel() + + assert.False(t, SupertypeTypeBound{Type: IntegerType}.HasInvalidType()) + assert.True(t, SupertypeTypeBound{Type: InvalidType}.HasInvalidType()) + }) + + t.Run("strict supertype", func(t *testing.T) { + + t.Parallel() + + assert.False(t, StrictSupertypeTypeBound{Type: IntegerType}.HasInvalidType()) + assert.True(t, StrictSupertypeTypeBound{Type: InvalidType}.HasInvalidType()) + }) + + t.Run("conjunction", func(t *testing.T) { + + t.Parallel() + + assert.False(t, + ConjunctionTypeBound{ + TypeBounds: []TypeBound{ + SubtypeTypeBound{Type: IntegerType}, + }, + }.HasInvalidType(), + ) + + assert.True(t, + ConjunctionTypeBound{ + TypeBounds: []TypeBound{ + SubtypeTypeBound{Type: InvalidType}, + }, + }.HasInvalidType(), + ) + }) +} diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 6a0b9b219a..74c4ab7725 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -186,7 +186,9 @@ var getAuthAccountFunctionType = func() *sema.FunctionType { typeParam := &sema.TypeParameter{ Name: "T", - TypeBound: sema.AccountReferenceType, + TypeBound: sema.SubtypeTypeBound{ + Type: sema.AccountReferenceType, + }, } return &sema.FunctionType{ diff --git a/runtime/stdlib/random.go b/runtime/stdlib/random.go index 46e6c821d9..117b991132 100644 --- a/runtime/stdlib/random.go +++ b/runtime/stdlib/random.go @@ -39,7 +39,9 @@ Follow best practices to prevent security issues when using this function var revertibleRandomFunctionType = func() *sema.FunctionType { typeParameter := &sema.TypeParameter{ Name: "T", - TypeBound: sema.FixedSizeUnsignedIntegerType, + TypeBound: sema.SubtypeTypeBound{ + Type: sema.FixedSizeUnsignedIntegerType, + }, } typeAnnotation := sema.NewTypeAnnotation( diff --git a/runtime/stdlib/random_test.go b/runtime/stdlib/random_test.go index 1f2c0af529..587429a6fd 100644 --- a/runtime/stdlib/random_test.go +++ b/runtime/stdlib/random_test.go @@ -54,7 +54,7 @@ func TestRandomBasicUniformityWithModulo(t *testing.T) { } testTypes := func(t *testing.T, testType func(*testing.T, sema.Type)) { - for _, ty := range sema.AllFixedSizeUnsignedIntegerTypes { + for _, ty := range sema.AllLeafFixedSizeUnsignedIntegerTypes { tyCopy := ty t.Run(ty.String(), func(t *testing.T) { t.Parallel() diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index b4339116c1..9bc99c5555 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -40,7 +40,9 @@ const inclusiveRangeConstructorFunctionDocString = ` var inclusiveRangeConstructorFunctionType = func() *sema.FunctionType { typeParameter := &sema.TypeParameter{ Name: "T", - TypeBound: sema.IntegerType, + TypeBound: sema.SubtypeTypeBound{ + Type: sema.IntegerType, + }, } typeAnnotation := sema.NewTypeAnnotation( diff --git a/runtime/stdlib/test_contract.go b/runtime/stdlib/test_contract.go index 3e8b17c5f2..b5fd748ab3 100644 --- a/runtime/stdlib/test_contract.go +++ b/runtime/stdlib/test_contract.go @@ -233,9 +233,11 @@ const testTypeExpectFunctionName = "expect" func newTestTypeExpectFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { typeParameter := &sema.TypeParameter{ - TypeBound: sema.AnyStructType, - Name: "T", - Optional: true, + TypeBound: sema.SubtypeTypeBound{ + Type: sema.AnyStructType, + }, + Name: "T", + Optional: true, } return &sema.FunctionType{ @@ -399,9 +401,11 @@ const testTypeNewMatcherFunctionName = "newMatcher" func newTestTypeNewMatcherFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { typeParameter := &sema.TypeParameter{ - TypeBound: sema.AnyStructType, - Name: "T", - Optional: true, + TypeBound: sema.SubtypeTypeBound{ + Type: sema.AnyStructType, + }, + Name: "T", + Optional: true, } return &sema.FunctionType{ @@ -467,9 +471,11 @@ Returns a matcher that succeeds if the tested value is equal to the given value. func newTestTypeEqualFunctionType(matcherType *sema.CompositeType) *sema.FunctionType { typeParameter := &sema.TypeParameter{ - TypeBound: sema.AnyStructType, - Name: "T", - Optional: true, + TypeBound: sema.SubtypeTypeBound{ + Type: sema.AnyStructType, + }, + Name: "T", + Optional: true, } return &sema.FunctionType{ diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index ce51d534e1..9ab42d8edc 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -289,7 +289,7 @@ func TestTestNewMatcher(t *testing.T) { _, err := newTestContractInterpreter(t, script) errs := checker.RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run("custom matcher with explicit type", func(t *testing.T) { @@ -462,7 +462,7 @@ func TestTestEqualMatcher(t *testing.T) { require.Error(t, err) errs := checker.RequireCheckerErrors(t, err, 2) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) assert.IsType(t, &sema.TypeMismatchError{}, errs[1]) }) @@ -672,8 +672,8 @@ func TestTestEqualMatcher(t *testing.T) { _, err := newTestContractInterpreter(t, script) errs := checker.RequireCheckerErrors(t, err, 4) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) - assert.IsType(t, &sema.TypeMismatchError{}, errs[1]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[1]) assert.IsType(t, &sema.TypeMismatchError{}, errs[2]) assert.IsType(t, &sema.TypeMismatchError{}, errs[3]) }) @@ -706,8 +706,8 @@ func TestTestEqualMatcher(t *testing.T) { _, err := newTestContractInterpreter(t, script) errs := checker.RequireCheckerErrors(t, err, 3) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) - assert.IsType(t, &sema.TypeMismatchError{}, errs[1]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[1]) assert.IsType(t, &sema.TypeMismatchError{}, errs[2]) }) } @@ -1956,8 +1956,8 @@ func TestTestExpect(t *testing.T) { _, err := newTestContractInterpreter(t, script) errs := checker.RequireCheckerErrors(t, err, 2) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) - assert.IsType(t, &sema.TypeMismatchError{}, errs[1]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[1]) }) t.Run("resource with struct matcher", func(t *testing.T) { @@ -1982,7 +1982,7 @@ func TestTestExpect(t *testing.T) { _, err := newTestContractInterpreter(t, script) errs := checker.RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run("struct with resource matcher", func(t *testing.T) { @@ -2007,7 +2007,7 @@ func TestTestExpect(t *testing.T) { _, err := newTestContractInterpreter(t, script) errs := checker.RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) } diff --git a/runtime/tests/checker/account_test.go b/runtime/tests/checker/account_test.go index 046bc5f6f8..93eb665a8a 100644 --- a/runtime/tests/checker/account_test.go +++ b/runtime/tests/checker/account_test.go @@ -299,11 +299,11 @@ func TestCheckAccountStorageSave(t *testing.T) { if domain == common.PathDomainStorage { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) } else { errs := RequireCheckerErrors(t, err, 2) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) require.IsType(t, &sema.TypeMismatchError{}, errs[1]) } }) @@ -330,11 +330,11 @@ func TestCheckAccountStorageSave(t *testing.T) { if domain == common.PathDomainStorage { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) } else { errs := RequireCheckerErrors(t, err, 2) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) require.IsType(t, &sema.TypeMismatchError{}, errs[1]) } }) @@ -623,12 +623,12 @@ func TestCheckAccountStorageCopy(t *testing.T) { if domain == common.PathDomainStorage { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) } else { errs := RequireCheckerErrors(t, err, 2) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) require.IsType(t, &sema.TypeMismatchError{}, errs[1]) } }) @@ -838,11 +838,11 @@ func TestCheckAccountStorageBorrow(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) } else { errs := RequireCheckerErrors(t, err, 2) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) require.IsType(t, &sema.TypeMismatchError{}, errs[1]) } }) @@ -868,11 +868,11 @@ func TestCheckAccountStorageBorrow(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) } else { errs := RequireCheckerErrors(t, err, 2) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) require.IsType(t, &sema.TypeMismatchError{}, errs[1]) } }) diff --git a/runtime/tests/checker/builtinfunctions_test.go b/runtime/tests/checker/builtinfunctions_test.go index eede313982..2ce2d42778 100644 --- a/runtime/tests/checker/builtinfunctions_test.go +++ b/runtime/tests/checker/builtinfunctions_test.go @@ -333,15 +333,9 @@ func TestCheckRevertibleRandom(t *testing.T) { }) } - for _, ty := range sema.AllFixedSizeUnsignedIntegerTypes { - switch ty { - case sema.FixedSizeUnsignedIntegerType: - continue - - default: - runValidCaseWithoutModulo(t, ty) - runValidCaseWithModulo(t, ty) - } + for _, ty := range sema.AllLeafFixedSizeUnsignedIntegerTypes { + runValidCaseWithoutModulo(t, ty) + runValidCaseWithModulo(t, ty) } runInvalidCase( @@ -349,7 +343,7 @@ func TestCheckRevertibleRandom(t *testing.T) { "revertibleRandom", "let rand = revertibleRandom()", []error{ - &sema.TypeMismatchError{}, + &sema.TypeBoundError{}, }, ) @@ -358,7 +352,7 @@ func TestCheckRevertibleRandom(t *testing.T) { "revertibleRandom", `let rand = revertibleRandom(modulo: "abcd")`, []error{ - &sema.TypeMismatchError{}, + &sema.TypeBoundError{}, }, ) diff --git a/runtime/tests/checker/capability_test.go b/runtime/tests/checker/capability_test.go index 35b6fc5b73..146aed9e5f 100644 --- a/runtime/tests/checker/capability_test.go +++ b/runtime/tests/checker/capability_test.go @@ -278,7 +278,7 @@ func TestCheckCapability_borrow(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run("struct", func(t *testing.T) { @@ -294,7 +294,7 @@ func TestCheckCapability_borrow(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) }) }) } @@ -456,7 +456,7 @@ func TestCheckCapability_check(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run("struct", func(t *testing.T) { @@ -472,7 +472,7 @@ func TestCheckCapability_check(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) }) }) } diff --git a/runtime/tests/checker/entitlements_test.go b/runtime/tests/checker/entitlements_test.go index 7067895fc4..c8259f938b 100644 --- a/runtime/tests/checker/entitlements_test.go +++ b/runtime/tests/checker/entitlements_test.go @@ -3284,7 +3284,7 @@ func TestCheckEntitlementTypeAnnotation(t *testing.T) { require.IsType(t, &sema.DirectEntitlementAnnotationError{}, errs[0]) // entitlements are not storable either - require.IsType(t, &sema.TypeMismatchError{}, errs[1]) + require.IsType(t, &sema.TypeBoundError{}, errs[1]) }) t.Run("intersection", func(t *testing.T) { @@ -3526,7 +3526,7 @@ func TestCheckEntitlementMappingTypeAnnotation(t *testing.T) { require.IsType(t, &sema.DirectEntitlementAnnotationError{}, errs[0]) // entitlements are not storable either - require.IsType(t, &sema.TypeMismatchError{}, errs[1]) + require.IsType(t, &sema.TypeBoundError{}, errs[1]) }) t.Run("intersection", func(t *testing.T) { diff --git a/runtime/tests/checker/fixedpoint_test.go b/runtime/tests/checker/fixedpoint_test.go index 18d1be7708..9605922162 100644 --- a/runtime/tests/checker/fixedpoint_test.go +++ b/runtime/tests/checker/fixedpoint_test.go @@ -122,12 +122,7 @@ func TestCheckFixedPointLiteralRanges(t *testing.T) { return RequireGlobalValue(t, checker.Elaboration, "x") } - for _, ty := range sema.AllFixedPointTypes { - // Only test leaf types - switch ty { - case sema.FixedPointType, sema.SignedFixedPointType: - continue - } + for _, ty := range sema.AllLeafFixedPointTypes { t.Run(ty.String(), func(t *testing.T) { @@ -553,7 +548,7 @@ func TestCheckSignedFixedPointNegate(t *testing.T) { t.Parallel() - for _, ty := range sema.AllSignedFixedPointTypes { + for _, ty := range sema.AllLeafSignedFixedPointTypes { name := ty.String() t.Run(name, func(t *testing.T) { @@ -576,7 +571,7 @@ func TestCheckInvalidUnsignedFixedPointNegate(t *testing.T) { t.Parallel() - for _, ty := range sema.AllUnsignedFixedPointTypes { + for _, ty := range sema.AllLeafUnsignedFixedPointTypes { t.Run(ty.String(), func(t *testing.T) { @@ -600,7 +595,7 @@ func TestCheckInvalidNegativeZeroUnsignedFixedPoint(t *testing.T) { t.Parallel() - for _, ty := range sema.AllUnsignedFixedPointTypes { + for _, ty := range sema.AllLeafUnsignedFixedPointTypes { t.Run(ty.String(), func(t *testing.T) { @@ -623,12 +618,7 @@ func TestCheckFixedPointLiteralScales(t *testing.T) { t.Parallel() - for _, ty := range sema.AllFixedPointTypes { - // Only test leaf types - switch ty { - case sema.FixedPointType, sema.SignedFixedPointType: - continue - } + for _, ty := range sema.AllLeafFixedPointTypes { t.Run(ty.String(), func(t *testing.T) { @@ -697,12 +687,8 @@ func TestCheckFixedPointMinMax(t *testing.T) { ) } - for _, ty := range sema.AllFixedPointTypes { - // Only test leaf types - switch ty { - case sema.FixedPointType, sema.SignedFixedPointType: - continue - } + for _, ty := range sema.AllLeafFixedPointTypes { + t.Run(ty.String(), func(t *testing.T) { test(t, ty) diff --git a/runtime/tests/checker/for_test.go b/runtime/tests/checker/for_test.go index 04a5294e3c..c377264e42 100644 --- a/runtime/tests/checker/for_test.go +++ b/runtime/tests/checker/for_test.go @@ -120,15 +120,7 @@ func TestCheckForInclusiveRange(t *testing.T) { }) } - for _, typ := range sema.AllIntegerTypes { - // Only test leaf integer types - switch typ { - case sema.IntegerType, - sema.SignedIntegerType, - sema.FixedSizeUnsignedIntegerType: - continue - } - + for _, typ := range sema.AllLeafIntegerTypes { test(typ) } diff --git a/runtime/tests/checker/genericfunction_test.go b/runtime/tests/checker/genericfunction_test.go index 22559c9acf..58d7953d1c 100644 --- a/runtime/tests/checker/genericfunction_test.go +++ b/runtime/tests/checker/genericfunction_test.go @@ -567,8 +567,10 @@ func TestCheckGenericFunctionInvocation(t *testing.T) { t.Parallel() typeParameter := &sema.TypeParameter{ - Name: "T", - TypeBound: sema.NumberType, + Name: "T", + TypeBound: sema.SubtypeTypeBound{ + Type: sema.NumberType, + }, } checker, err := parseAndCheckWithTestValue(t, @@ -605,8 +607,10 @@ func TestCheckGenericFunctionInvocation(t *testing.T) { t.Parallel() typeParameter := &sema.TypeParameter{ - Name: "T", - TypeBound: sema.NumberType, + Name: "T", + TypeBound: sema.SubtypeTypeBound{ + Type: sema.NumberType, + }, } _, err := parseAndCheckWithTestValue(t, @@ -623,7 +627,7 @@ func TestCheckGenericFunctionInvocation(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run("invalid: one type parameter with type bound, no type argument, one parameter, one argument: bound not satisfied", func(t *testing.T) { @@ -631,8 +635,10 @@ func TestCheckGenericFunctionInvocation(t *testing.T) { t.Parallel() typeParameter := &sema.TypeParameter{ - Name: "T", - TypeBound: sema.NumberType, + Name: "T", + TypeBound: sema.SubtypeTypeBound{ + Type: sema.NumberType, + }, } _, err := parseAndCheckWithTestValue(t, @@ -660,7 +666,7 @@ func TestCheckGenericFunctionInvocation(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run("valid: one type parameter, one type argument, no parameters, no arguments, generic return type", func(t *testing.T) { @@ -712,8 +718,10 @@ func TestCheckGenericFunctionInvocation(t *testing.T) { t.Run(test.name, func(t *testing.T) { typeParameter := &sema.TypeParameter{ - Name: "T", - TypeBound: sema.NumberType, + Name: "T", + TypeBound: sema.SubtypeTypeBound{ + Type: sema.NumberType, + }, } checker, err := parseAndCheckWithTestValue(t, @@ -802,8 +810,10 @@ func TestCheckGenericFunctionInvocation(t *testing.T) { t.Run(test.name, func(t *testing.T) { typeParameter := &sema.TypeParameter{ - Name: "T", - TypeBound: sema.NumberType, + Name: "T", + TypeBound: sema.SubtypeTypeBound{ + Type: sema.NumberType, + }, } checker, err := parseAndCheckWithTestValue(t, @@ -1115,6 +1125,6 @@ func TestCheckGenericFunctionDeclaration(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeBoundError{}, errs[0]) }) } diff --git a/runtime/tests/checker/integer_test.go b/runtime/tests/checker/integer_test.go index 7051db107b..9e1db319e2 100644 --- a/runtime/tests/checker/integer_test.go +++ b/runtime/tests/checker/integer_test.go @@ -409,7 +409,7 @@ func TestCheckSignedIntegerNegate(t *testing.T) { t.Parallel() - for _, ty := range sema.AllSignedIntegerTypes { + for _, ty := range sema.AllLeafSignedIntegerTypes { name := ty.String() t.Run(name, func(t *testing.T) { @@ -431,7 +431,7 @@ func TestCheckInvalidUnsignedIntegerNegate(t *testing.T) { t.Parallel() - for _, ty := range sema.AllUnsignedIntegerTypes { + for _, ty := range sema.AllLeafUnsignedIntegerTypes { name := ty.String() t.Run(name, func(t *testing.T) { @@ -485,12 +485,7 @@ func TestCheckFixedPointToIntegerConversion(t *testing.T) { t.Parallel() - for _, ty := range sema.AllIntegerTypes { - // Only test leaf types - switch ty { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } + for _, ty := range sema.AllLeafIntegerTypes { t.Run(ty.String(), func(t *testing.T) { @@ -565,12 +560,7 @@ func TestCheckIntegerMinMax(t *testing.T) { ) } - for _, ty := range sema.AllIntegerTypes { - // Only test leaf types - switch ty { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } + for _, ty := range sema.AllLeafIntegerTypes { t.Run(ty.String(), func(t *testing.T) { numericType := ty.(*sema.NumericType) diff --git a/runtime/tests/checker/operations_test.go b/runtime/tests/checker/operations_test.go index 38c02bcc7f..6edd4fac7d 100644 --- a/runtime/tests/checker/operations_test.go +++ b/runtime/tests/checker/operations_test.go @@ -448,8 +448,8 @@ func TestCheckSaturatedArithmeticFunctions(t *testing.T) { } for _, ty := range common.Concat( - sema.AllSignedIntegerTypes, - sema.AllSignedFixedPointTypes, + sema.AllLeafSignedIntegerTypes, + sema.AllLeafSignedFixedPointTypes, ) { if ty == sema.IntType { @@ -466,8 +466,8 @@ func TestCheckSaturatedArithmeticFunctions(t *testing.T) { } for _, ty := range common.Concat( - sema.AllUnsignedIntegerTypes, - sema.AllUnsignedFixedPointTypes, + sema.AllLeafUnsignedIntegerTypes, + sema.AllLeafUnsignedFixedPointTypes, ) { if ty == sema.UIntType || strings.HasPrefix(ty.String(), "Word") { diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 4e64dd56fc..b5963727a6 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -306,14 +306,7 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { }) } - for _, integerType := range sema.AllIntegerTypes { - // Only test leaf types - switch integerType { - case sema.IntegerType, - sema.SignedIntegerType, - sema.FixedSizeUnsignedIntegerType: - continue - } + for _, integerType := range sema.AllLeafIntegerTypes { typeString := integerType.String() diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 060c983961..2b6402f730 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -140,7 +140,7 @@ func TestCheckTypeArguments(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run("capability, instantiation with two arguments", func(t *testing.T) { @@ -915,8 +915,10 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { &sema.FunctionType{ TypeParameters: []*sema.TypeParameter{ { - Name: "T", - TypeBound: &sema.InclusiveRangeType{}, + Name: "T", + TypeBound: sema.SubtypeTypeBound{ + Type: &sema.InclusiveRangeType{}, + }, }, }, ReturnTypeAnnotation: sema.VoidTypeAnnotation, @@ -936,8 +938,10 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { TypeParameters: []*sema.TypeParameter{ { Name: "T", - TypeBound: &sema.InclusiveRangeType{ - MemberType: sema.IntType, + TypeBound: sema.SubtypeTypeBound{ + Type: &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, }, }, }, diff --git a/runtime/tests/interpreter/arithmetic_test.go b/runtime/tests/interpreter/arithmetic_test.go index 84974e2d4a..358cd22571 100644 --- a/runtime/tests/interpreter/arithmetic_test.go +++ b/runtime/tests/interpreter/arithmetic_test.go @@ -725,8 +725,8 @@ func TestInterpretSaturatedArithmeticFunctions(t *testing.T) { // Verify all test cases exist for _, ty := range common.Concat( - sema.AllSignedIntegerTypes, - sema.AllSignedFixedPointTypes, + sema.AllLeafSignedIntegerTypes, + sema.AllLeafSignedFixedPointTypes, ) { testCase, ok := testCases[ty] @@ -751,8 +751,8 @@ func TestInterpretSaturatedArithmeticFunctions(t *testing.T) { } for _, ty := range common.Concat( - sema.AllUnsignedIntegerTypes, - sema.AllUnsignedFixedPointTypes, + sema.AllLeafUnsignedIntegerTypes, + sema.AllLeafUnsignedFixedPointTypes, ) { if strings.HasPrefix(ty.String(), "Word") { diff --git a/runtime/tests/interpreter/fixedpoint_test.go b/runtime/tests/interpreter/fixedpoint_test.go index b099d8519e..37c32f49a5 100644 --- a/runtime/tests/interpreter/fixedpoint_test.go +++ b/runtime/tests/interpreter/fixedpoint_test.go @@ -59,12 +59,7 @@ func TestInterpretFixedPointConversionAndAddition(t *testing.T) { "UFix64": interpreter.NewUnmeteredUFix64Value(123000000), } - for _, fixedPointType := range sema.AllFixedPointTypes { - // Only test leaf types - switch fixedPointType { - case sema.FixedPointType, sema.SignedFixedPointType: - continue - } + for _, fixedPointType := range sema.AllLeafFixedPointTypes { if _, ok := tests[fixedPointType.String()]; !ok { panic(fmt.Sprintf("broken test: missing %s", fixedPointType)) @@ -117,13 +112,7 @@ var testFixedPointValues = map[string]interpreter.Value{ } func init() { - for _, fixedPointType := range sema.AllFixedPointTypes { - // Only test leaf types - switch fixedPointType { - case sema.FixedPointType, sema.SignedFixedPointType: - continue - } - + for _, fixedPointType := range sema.AllLeafFixedPointTypes { if _, ok := testFixedPointValues[fixedPointType.String()]; !ok { panic(fmt.Sprintf("broken test: missing fixed-point type: %s", fixedPointType)) } @@ -354,7 +343,7 @@ func TestInterpretFixedPointConversions(t *testing.T) { t.Run("invalid negative integer to UFix64", func(t *testing.T) { - for _, integerType := range sema.AllSignedIntegerTypes { + for _, integerType := range sema.AllLeafSignedIntegerTypes { t.Run(integerType.String(), func(t *testing.T) { @@ -500,7 +489,7 @@ func TestInterpretFixedPointConversions(t *testing.T) { const testedValue = sema.Fix64TypeMinInt - 1 testValueBig := big.NewInt(testedValue) - for _, integerType := range sema.AllSignedIntegerTypes { + for _, integerType := range sema.AllLeafSignedIntegerTypes { // Only test for integer types that can hold testedValue @@ -579,13 +568,7 @@ func TestInterpretFixedPointMinMax(t *testing.T) { }, } - for _, ty := range sema.AllFixedPointTypes { - // Only test leaf types - switch ty { - case sema.FixedPointType, sema.SignedFixedPointType: - continue - } - + for _, ty := range sema.AllLeafFixedPointTypes { if _, ok := testCases[ty]; !ok { require.Fail(t, "missing type: %s", ty.String()) } diff --git a/runtime/tests/interpreter/for_test.go b/runtime/tests/interpreter/for_test.go index 6763c8f89c..d5ee1ac0b9 100644 --- a/runtime/tests/interpreter/for_test.go +++ b/runtime/tests/interpreter/for_test.go @@ -851,26 +851,14 @@ func TestInclusiveRangeForInLoop(t *testing.T) { }) } - for _, typ := range sema.AllIntegerTypes { - // Only test leaf types - switch typ { - case sema.IntegerType, - sema.SignedIntegerType, - sema.FixedSizeUnsignedIntegerType: - continue - } + for _, typ := range sema.AllLeafIntegerTypes { for _, testCase := range unsignedTestCases { runTestCase(t, typ, testCase) } } - for _, typ := range sema.AllSignedIntegerTypes { - // Only test leaf types - switch typ { - case sema.SignedIntegerType: - continue - } + for _, typ := range sema.AllLeafSignedIntegerTypes { for _, testCase := range signedTestCases { runTestCase(t, typ, testCase) diff --git a/runtime/tests/interpreter/integers_test.go b/runtime/tests/interpreter/integers_test.go index 285b35dd63..ce93d70aa4 100644 --- a/runtime/tests/interpreter/integers_test.go +++ b/runtime/tests/interpreter/integers_test.go @@ -61,13 +61,7 @@ var testIntegerTypesAndValues = map[string]interpreter.Value{ } func init() { - for _, integerType := range sema.AllIntegerTypes { - // Only test leaf types - switch integerType { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } - + for _, integerType := range sema.AllLeafIntegerTypes { if _, ok := testIntegerTypesAndValues[integerType.String()]; !ok { panic(fmt.Sprintf("broken test: missing %s", integerType)) } @@ -702,13 +696,7 @@ func TestInterpretIntegerConversion(t *testing.T) { }, } - for _, ty := range sema.AllIntegerTypes { - // Only test leaf types - switch ty { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } - + for _, ty := range sema.AllLeafIntegerTypes { _, ok := testValues[ty.(*sema.NumericType)] require.True(t, ok, "missing expected value for type %s", ty.String()) } @@ -888,13 +876,7 @@ func TestInterpretIntegerMinMax(t *testing.T) { }, } - for _, ty := range sema.AllIntegerTypes { - // Only test leaf types - switch ty { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } - + for _, ty := range sema.AllLeafIntegerTypes { if _, ok := testCases[ty]; !ok { require.Fail(t, "missing type: %s", ty.String()) } @@ -959,7 +941,7 @@ func TestInterpretStringIntegerConversion(t *testing.T) { } } - for _, typ := range append(sema.AllSignedIntegerTypes, sema.AllUnsignedIntegerTypes...) { + for _, typ := range sema.AllLeafIntegerTypes { t.Run(typ.String(), func(t *testing.T) { test(t, typ) }) } } diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 1b2db69e1d..4983847bf2 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -7519,12 +7519,7 @@ func TestInterpretEmitEventParameterTypes(t *testing.T) { } } - for _, fixedPointType := range sema.AllFixedPointTypes { - - switch fixedPointType { - case sema.FixedPointType, sema.SignedFixedPointType: - continue - } + for _, fixedPointType := range sema.AllLeafFixedPointTypes { if _, ok := validTypes[fixedPointType.String()]; !ok { panic(fmt.Sprintf("broken test: missing %s", fixedPointType)) diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index 93bb45c80b..f9c8c8b15e 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -529,14 +529,7 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { }) } - for _, integerType := range sema.AllIntegerTypes { - // Only test leaf types - switch integerType { - case sema.IntegerType, - sema.SignedIntegerType, - sema.FixedSizeUnsignedIntegerType: - continue - } + for _, integerType := range sema.AllLeafIntegerTypes { typeString := integerType.String() @@ -560,12 +553,7 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { } // Additional invalid cases for signed integer types - for _, integerType := range sema.AllSignedIntegerTypes { - // Only test leaf types - switch integerType { - case sema.SignedIntegerType: - continue - } + for _, integerType := range sema.AllLeafSignedIntegerTypes { typeString := integerType.String() @@ -582,12 +570,7 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { } // Additional invalid cases for unsigned integer types - for _, integerType := range sema.AllUnsignedIntegerTypes { - // Only test leaf types - switch integerType { - case sema.IntegerType: - continue - } + for _, integerType := range sema.AllLeafUnsignedIntegerTypes { typeString := integerType.String() diff --git a/runtime/tests/interpreter/reference_test.go b/runtime/tests/interpreter/reference_test.go index 6077a9a44a..c110f4fb8e 100644 --- a/runtime/tests/interpreter/reference_test.go +++ b/runtime/tests/interpreter/reference_test.go @@ -1893,12 +1893,7 @@ func TestInterpretDereference(t *testing.T) { sema.Int256Type: interpreter.NewUnmeteredInt256ValueFromInt64(42), } - for _, typ := range sema.AllIntegerTypes { - // Only test leaf types - switch typ { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } + for _, typ := range sema.AllLeafIntegerTypes { integerType := typ typString := typ.QualifiedString() @@ -1930,12 +1925,7 @@ func TestInterpretDereference(t *testing.T) { sema.Fix64Type: interpreter.NewUnmeteredFix64Value(4224_000_000), } - for _, typ := range sema.AllFixedPointTypes { - // Only test leaf types - switch typ { - case sema.FixedPointType, sema.SignedFixedPointType: - continue - } + for _, typ := range sema.AllLeafFixedPointTypes { fixedPointType := typ typString := typ.QualifiedString() @@ -1962,12 +1952,7 @@ func TestInterpretDereference(t *testing.T) { t.Run("Variable-sized array of integers", func(t *testing.T) { t.Parallel() - for _, typ := range sema.AllIntegerTypes { - // Only test leaf types - switch typ { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } + for _, typ := range sema.AllLeafIntegerTypes { integerType := typ typString := typ.QualifiedString() @@ -2379,12 +2364,7 @@ func TestInterpretDereference(t *testing.T) { t.Run("Constant-sized array of integers", func(t *testing.T) { t.Parallel() - for _, typ := range sema.AllIntegerTypes { - // Only test leaf types - switch typ { - case sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType: - continue - } + for _, typ := range sema.AllLeafIntegerTypes { integerType := typ typString := typ.QualifiedString() From e0640f57fe76427cf4fab594d07709f2e30a4cc9 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 2 Apr 2024 10:32:34 -0400 Subject: [PATCH 02/15] refactor for composability --- runtime/account_test.go | 2 +- runtime/sema/check_binary_expression.go | 2 +- runtime/sema/type.go | 4 +- runtime/sema/typebound.go | 237 +++++++++++------------- runtime/sema/typebound_test.go | 55 +++--- 5 files changed, 150 insertions(+), 150 deletions(-) diff --git a/runtime/account_test.go b/runtime/account_test.go index 54d37cbf53..2330ce4ddc 100644 --- a/runtime/account_test.go +++ b/runtime/account_test.go @@ -184,7 +184,7 @@ func TestRuntimeStoreAccountAPITypes(t *testing.T) { RequireError(t, err) - assert.Contains(t, err.Error(), "expected `Storable`") + assert.Contains(t, err.Error(), "expected type satisfying {Storable}") } } diff --git a/runtime/sema/check_binary_expression.go b/runtime/sema/check_binary_expression.go index 6155ad8f63..b4f77fbc2d 100644 --- a/runtime/sema/check_binary_expression.go +++ b/runtime/sema/check_binary_expression.go @@ -118,7 +118,7 @@ func (checker *Checker) VisitBinaryExpression(expression *ast.BinaryExpression) // ``` if expectedType == nil || - (leftType != expectedType && IsStrictSubType(leftType, expectedType)) { + (leftType != expectedType && IsProperSubType(leftType, expectedType)) { expectedType = leftType } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 37d2260dda..9430b3d2cb 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -7419,11 +7419,11 @@ func IsSameTypeKind(subType Type, superType Type) bool { return IsSubType(subType, superType) } -// IsStrictSubType is similar to IsSubType, +// IsProperSubType is similar to IsSubType, // i.e. it determines if the given subtype is a subtype // of the given supertype, but returns false // if the subtype and supertype refer to the same type. -func IsStrictSubType(subType Type, superType Type) bool { +func IsProperSubType(subType Type, superType Type) bool { if subType.Equal(superType) { return false diff --git a/runtime/sema/typebound.go b/runtime/sema/typebound.go index 58ea88243e..d6ee4eba68 100644 --- a/runtime/sema/typebound.go +++ b/runtime/sema/typebound.go @@ -44,7 +44,8 @@ type TypeBound interface { RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) } -// SubtypeTypeBound +// SubtypeTypeBound(T) expresses the requirement that +// ∀U, U <= T type SubtypeTypeBound struct { Type Type @@ -52,6 +53,10 @@ type SubtypeTypeBound struct { var _ TypeBound = SubtypeTypeBound{} +func NewSubtypeTypeBound(ty Type) TypeBound { + return SubtypeTypeBound{Type: ty} +} + func (SubtypeTypeBound) isTypeBound() {} func (b SubtypeTypeBound) Satisfies(ty Type) bool { @@ -102,33 +107,38 @@ func (b SubtypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewr return b, false } -// StrictSubtypeTypeBound +// EqualTypeBound expresses the requirement that +// ∀U, U = T -type StrictSubtypeTypeBound struct { +type EqualTypeBound struct { Type Type } -var _ TypeBound = StrictSubtypeTypeBound{} +var _ TypeBound = EqualTypeBound{} + +func NewEqualTypeBound(ty Type) TypeBound { + return EqualTypeBound{Type: ty} +} -func (StrictSubtypeTypeBound) isTypeBound() {} +func (EqualTypeBound) isTypeBound() {} -func (b StrictSubtypeTypeBound) Satisfies(ty Type) bool { - return IsStrictSubType(ty, b.Type) +func (b EqualTypeBound) Satisfies(ty Type) bool { + return ty.Equal(b.Type) } -func (b StrictSubtypeTypeBound) HasInvalidType() bool { +func (b EqualTypeBound) HasInvalidType() bool { return b.Type.IsInvalidType() } -func (b StrictSubtypeTypeBound) Equal(bound TypeBound) bool { - other, ok := bound.(StrictSubtypeTypeBound) +func (b EqualTypeBound) Equal(bound TypeBound) bool { + other, ok := bound.(EqualTypeBound) if !ok { return false } return b.Type.Equal(other.Type) } -func (b StrictSubtypeTypeBound) CheckInstantiated( +func (b EqualTypeBound) CheckInstantiated( pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error), @@ -136,31 +146,95 @@ func (b StrictSubtypeTypeBound) CheckInstantiated( b.Type.CheckInstantiated(pos, memoryGauge, report) } -func (b StrictSubtypeTypeBound) Map( +func (b EqualTypeBound) Map( gauge common.MemoryGauge, typeParamMap map[*TypeParameter]*TypeParameter, f func(Type) Type, ) TypeBound { - return SubtypeTypeBound{ + return EqualTypeBound{ Type: b.Type.Map(gauge, typeParamMap, f), } } -func (b StrictSubtypeTypeBound) TypeAnnotationState() TypeAnnotationState { +func (b EqualTypeBound) TypeAnnotationState() TypeAnnotationState { return b.Type.TypeAnnotationState() } -func (b StrictSubtypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { +func (b EqualTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { rewrittenType, rewritten := b.Type.RewriteWithIntersectionTypes() if rewritten { - return StrictSubtypeTypeBound{ + return EqualTypeBound{ Type: rewrittenType, }, true } return b, false } -// ConjunctionTypeBound +// NegationTypeBound(B) expresses the requirement that +// ∀U, !B(U) + +type NegationTypeBound struct { + NegatedBound TypeBound +} + +var _ TypeBound = NegationTypeBound{} + +func NewNegationTypeBound(bound TypeBound) TypeBound { + return NegationTypeBound{NegatedBound: bound} +} + +func (NegationTypeBound) isTypeBound() {} + +func (b NegationTypeBound) Satisfies(ty Type) bool { + return !b.NegatedBound.Satisfies(ty) +} + +func (b NegationTypeBound) HasInvalidType() bool { + return b.NegatedBound.HasInvalidType() +} + +func (b NegationTypeBound) Equal(bound TypeBound) bool { + other, ok := bound.(NegationTypeBound) + if !ok { + return false + } + return b.NegatedBound.Equal(other.NegatedBound) +} + +func (b NegationTypeBound) CheckInstantiated( + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), +) { + b.NegatedBound.CheckInstantiated(pos, memoryGauge, report) +} + +func (b NegationTypeBound) Map( + gauge common.MemoryGauge, + typeParamMap map[*TypeParameter]*TypeParameter, + f func(Type) Type, +) TypeBound { + return NegationTypeBound{ + NegatedBound: b.NegatedBound.Map(gauge, typeParamMap, f), + } +} + +func (b NegationTypeBound) TypeAnnotationState() TypeAnnotationState { + return b.NegatedBound.TypeAnnotationState() +} + +func (b NegationTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { + rewrittenBound, rewritten := b.NegatedBound.RewriteWithIntersectionTypes() + if rewritten { + return NegationTypeBound{ + NegatedBound: rewrittenBound, + }, true + } + return b, false +} + +// ConjunctionTypeBound(B1, ..., Bn) expresses the requirement that +// ∀U, B1(U) & ... & Bn(U) type ConjunctionTypeBound struct { TypeBounds []TypeBound @@ -168,6 +242,10 @@ type ConjunctionTypeBound struct { var _ TypeBound = ConjunctionTypeBound{} +func NewConjunctionTypeBound(typeBounds []TypeBound) TypeBound { + return ConjunctionTypeBound{TypeBounds: typeBounds} +} + func (ConjunctionTypeBound) isTypeBound() {} func (b ConjunctionTypeBound) Satisfies(ty Type) bool { @@ -265,118 +343,29 @@ func (b ConjunctionTypeBound) RewriteWithIntersectionTypes() (result TypeBound, } } -// SupertypeTypeBound - -type SupertypeTypeBound struct { - Type Type -} - -var _ TypeBound = SupertypeTypeBound{} +// Any other kinds of type bounds we might wish to express can be +// written as the composition of `<=`, `=`, `!` and `&`. Technically, `=` is not +// really even necessary, as `U = T` is equivalent to `U <= T & T <= U`, but for +// performance reasons we give it its own basic bound -func (SupertypeTypeBound) isTypeBound() {} - -func (b SupertypeTypeBound) Satisfies(ty Type) bool { - return IsSubType(b.Type, ty) +// `U <= T && !(T = U) ==> U < T` +func NewStrictSubtypeTypeBound(ty Type) TypeBound { + subtypeBound := NewSubtypeTypeBound(ty) + nonEqualBound := NewNegationTypeBound(NewEqualTypeBound(ty)) + return NewConjunctionTypeBound([]TypeBound{subtypeBound, nonEqualBound}) } -func (b SupertypeTypeBound) HasInvalidType() bool { - return b.Type.IsInvalidType() +// `!(U <= T) ==> U > T` +func NewStrictSupertypeTypeBound(ty Type) TypeBound { + return NewNegationTypeBound(NewSubtypeTypeBound(ty)) } -func (b SupertypeTypeBound) Equal(bound TypeBound) bool { - other, ok := bound.(SupertypeTypeBound) - if !ok { - return false - } - return b.Type.Equal(other.Type) -} - -func (b SupertypeTypeBound) CheckInstantiated( - pos ast.HasPosition, - memoryGauge common.MemoryGauge, - report func(err error), -) { - b.Type.CheckInstantiated(pos, memoryGauge, report) +// `!(U < T) ==> U >= T` +func NewSupertypeTypeBound(ty Type) TypeBound { + return NewNegationTypeBound(NewStrictSubtypeTypeBound(ty)) } -func (b SupertypeTypeBound) Map( - gauge common.MemoryGauge, - typeParamMap map[*TypeParameter]*TypeParameter, - f func(Type) Type, -) TypeBound { - return SupertypeTypeBound{ - Type: b.Type.Map(gauge, typeParamMap, f), - } -} - -func (b SupertypeTypeBound) TypeAnnotationState() TypeAnnotationState { - return b.Type.TypeAnnotationState() -} - -func (b SupertypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { - rewrittenType, rewritten := b.Type.RewriteWithIntersectionTypes() - if rewritten { - return SupertypeTypeBound{ - Type: rewrittenType, - }, true - } - return b, false -} - -// StrictSupertypeTypeBound - -type StrictSupertypeTypeBound struct { - Type Type -} - -var _ TypeBound = StrictSupertypeTypeBound{} - -func (StrictSupertypeTypeBound) isTypeBound() {} - -func (b StrictSupertypeTypeBound) Satisfies(ty Type) bool { - return IsStrictSubType(b.Type, ty) -} - -func (b StrictSupertypeTypeBound) HasInvalidType() bool { - return b.Type.IsInvalidType() -} - -func (b StrictSupertypeTypeBound) Equal(bound TypeBound) bool { - other, ok := bound.(StrictSupertypeTypeBound) - if !ok { - return false - } - return b.Type.Equal(other.Type) -} - -func (b StrictSupertypeTypeBound) CheckInstantiated( - pos ast.HasPosition, - memoryGauge common.MemoryGauge, - report func(err error), -) { - b.Type.CheckInstantiated(pos, memoryGauge, report) -} - -func (b StrictSupertypeTypeBound) Map( - gauge common.MemoryGauge, - typeParamMap map[*TypeParameter]*TypeParameter, - f func(Type) Type, -) TypeBound { - return StrictSupertypeTypeBound{ - Type: b.Type.Map(gauge, typeParamMap, f), - } -} - -func (b StrictSupertypeTypeBound) TypeAnnotationState() TypeAnnotationState { - return b.Type.TypeAnnotationState() -} - -func (b StrictSupertypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { - rewrittenType, rewritten := b.Type.RewriteWithIntersectionTypes() - if rewritten { - return StrictSupertypeTypeBound{ - Type: rewrittenType, - }, true - } - return b, false +// `!(B1 & ... & Bn) ==> B1 || ... || Bn` +func NewDisjunctionTypeBound(typeBounds []TypeBound) TypeBound { + return NewNegationTypeBound(NewConjunctionTypeBound(typeBounds)) } diff --git a/runtime/sema/typebound_test.go b/runtime/sema/typebound_test.go index 7dc0dcc577..1f63ab52b0 100644 --- a/runtime/sema/typebound_test.go +++ b/runtime/sema/typebound_test.go @@ -31,7 +31,7 @@ func TestTypeBound_Satisfies(t *testing.T) { t.Parallel() - typeBound := SubtypeTypeBound{Type: IntegerType} + typeBound := NewSubtypeTypeBound(IntegerType) assert.True(t, typeBound.Satisfies(IntegerType)) assert.True(t, typeBound.Satisfies(NeverType)) @@ -45,7 +45,7 @@ func TestTypeBound_Satisfies(t *testing.T) { t.Parallel() - typeBound := StrictSubtypeTypeBound{Type: IntegerType} + typeBound := NewStrictSubtypeTypeBound(IntegerType) assert.False(t, typeBound.Satisfies(IntegerType)) assert.True(t, typeBound.Satisfies(NeverType)) @@ -59,7 +59,7 @@ func TestTypeBound_Satisfies(t *testing.T) { t.Parallel() - typeBound := SupertypeTypeBound{Type: NeverType} + typeBound := NewSupertypeTypeBound(NeverType) assert.True(t, typeBound.Satisfies(NeverType)) assert.True(t, typeBound.Satisfies(IntegerType)) @@ -73,7 +73,7 @@ func TestTypeBound_Satisfies(t *testing.T) { t.Parallel() - typeBound := StrictSupertypeTypeBound{Type: NeverType} + typeBound := NewStrictSupertypeTypeBound(NeverType) assert.False(t, typeBound.Satisfies(NeverType)) assert.True(t, typeBound.Satisfies(IntegerType)) @@ -87,16 +87,10 @@ func TestTypeBound_Satisfies(t *testing.T) { t.Parallel() - typeBound := ConjunctionTypeBound{ - TypeBounds: []TypeBound{ - StrictSupertypeTypeBound{ - Type: NeverType, - }, - StrictSubtypeTypeBound{ - Type: FixedSizeUnsignedIntegerType, - }, - }, - } + typeBound := NewConjunctionTypeBound([]TypeBound{ + NewStrictSupertypeTypeBound(NeverType), + NewStrictSubtypeTypeBound(FixedSizeUnsignedIntegerType), + }) assert.False(t, typeBound.Satisfies(FixedSizeUnsignedIntegerType)) assert.False(t, typeBound.Satisfies(NeverType)) @@ -105,6 +99,23 @@ func TestTypeBound_Satisfies(t *testing.T) { assert.True(t, typeBound.Satisfies(integerType)) } }) + + t.Run("disjunction", func(t *testing.T) { + + t.Parallel() + + typeBound := NewDisjunctionTypeBound([]TypeBound{ + NewStrictSupertypeTypeBound(NeverType), + NewStrictSubtypeTypeBound(NeverType), + }) + + assert.True(t, typeBound.Satisfies(FixedSizeUnsignedIntegerType)) + assert.True(t, typeBound.Satisfies(NeverType)) + + for _, integerType := range AllLeafFixedSizeUnsignedIntegerTypes { + assert.True(t, typeBound.Satisfies(integerType)) + } + }) } func TestTypeBound_HasInvalid(t *testing.T) { @@ -114,32 +125,32 @@ func TestTypeBound_HasInvalid(t *testing.T) { t.Parallel() - assert.False(t, SubtypeTypeBound{Type: IntegerType}.HasInvalidType()) - assert.True(t, SubtypeTypeBound{Type: InvalidType}.HasInvalidType()) + assert.False(t, NewSubtypeTypeBound(IntegerType).HasInvalidType()) + assert.True(t, NewSubtypeTypeBound(InvalidType).HasInvalidType()) }) t.Run("strict subtype", func(t *testing.T) { t.Parallel() - assert.False(t, StrictSubtypeTypeBound{Type: IntegerType}.HasInvalidType()) - assert.True(t, StrictSubtypeTypeBound{Type: InvalidType}.HasInvalidType()) + assert.False(t, NewStrictSubtypeTypeBound(IntegerType).HasInvalidType()) + assert.True(t, NewStrictSubtypeTypeBound(InvalidType).HasInvalidType()) }) t.Run("supertype", func(t *testing.T) { t.Parallel() - assert.False(t, SupertypeTypeBound{Type: IntegerType}.HasInvalidType()) - assert.True(t, SupertypeTypeBound{Type: InvalidType}.HasInvalidType()) + assert.False(t, NewSupertypeTypeBound(IntegerType).HasInvalidType()) + assert.True(t, NewSupertypeTypeBound(InvalidType).HasInvalidType()) }) t.Run("strict supertype", func(t *testing.T) { t.Parallel() - assert.False(t, StrictSupertypeTypeBound{Type: IntegerType}.HasInvalidType()) - assert.True(t, StrictSupertypeTypeBound{Type: InvalidType}.HasInvalidType()) + assert.False(t, NewStrictSupertypeTypeBound(IntegerType).HasInvalidType()) + assert.True(t, NewStrictSupertypeTypeBound(InvalidType).HasInvalidType()) }) t.Run("conjunction", func(t *testing.T) { From cc6fb76d4577e9cfb9499e308539dfc0e1b00955 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 2 Apr 2024 10:47:24 -0400 Subject: [PATCH 03/15] rewrite newly added type arguments checks with generalized bounds --- runtime/sema/account.go | 43 +++++-------------- runtime/stdlib/random.go | 36 +++------------- runtime/tests/checker/account_test.go | 4 +- .../tests/checker/builtinfunctions_test.go | 4 +- 4 files changed, 22 insertions(+), 65 deletions(-) diff --git a/runtime/sema/account.go b/runtime/sema/account.go index 75eba98d60..ec6598ace5 100644 --- a/runtime/sema/account.go +++ b/runtime/sema/account.go @@ -18,14 +18,6 @@ package sema -import ( - "fmt" - - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/errors" -) - //go:generate go run ./gen account.cdc account.gen.go var AccountTypeAnnotation = NewTypeAnnotation(AccountType) @@ -64,30 +56,17 @@ var FullyEntitledAccountReferenceTypeAnnotation = NewTypeAnnotation(FullyEntitle func init() { Account_ContractsTypeAddFunctionType.Arity = &Arity{Min: 2} - Account_CapabilitiesTypeGetFunctionType.TypeArgumentsCheck = - func(memoryGauge common.MemoryGauge, - typeArguments *TypeParameterTypeOrderedMap, - _ []*ast.TypeAnnotation, - invocationRange ast.HasPosition, - report func(err error), - ) { - typeArg, ok := typeArguments.Get(Account_CapabilitiesTypeGetFunctionTypeParameterT) - if !ok || typeArg == nil { - // checker should prevent this - panic(errors.NewUnreachableError()) - } - if typeArg == NeverType { - report(&InvalidTypeArgumentError{ - TypeArgumentName: Account_CapabilitiesTypeGetFunctionTypeParameterT.Name, - Range: ast.NewRangeFromPositioned(memoryGauge, invocationRange), - Details: fmt.Sprintf( - "Type argument for `%s` cannot be `%s`", - Account_CapabilitiesTypeGetFunctionName, - NeverType, - ), - }) - } - } + // capabilities.get has a strict supertype requirement that its type argument is not `Never`, + // but we can't yet express this in source syntax. + // TODO: if we add support for arbitrary logical type bounds to the source language, move this + // into the generator + Account_CapabilitiesTypeGetFunctionType.TypeParameters[0].TypeBound = + NewConjunctionTypeBound( + []TypeBound{ + Account_CapabilitiesTypeGetFunctionType.TypeParameters[0].TypeBound, + NewStrictSupertypeTypeBound(NeverType), + }, + ) addToBaseActivation(AccountMappingType) addToBaseActivation(CapabilitiesMappingType) diff --git a/runtime/stdlib/random.go b/runtime/stdlib/random.go index 02d7bc3241..3020a745c6 100644 --- a/runtime/stdlib/random.go +++ b/runtime/stdlib/random.go @@ -20,10 +20,8 @@ package stdlib import ( "encoding/binary" - "fmt" "math/big" - "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" @@ -42,10 +40,13 @@ const revertibleRandomFunctionName = "revertibleRandom" var revertibleRandomFunctionType = func() *sema.FunctionType { typeParameter := &sema.TypeParameter{ - Name: "T", - TypeBound: sema.SubtypeTypeBound{ - Type: sema.FixedSizeUnsignedIntegerType, - }, + Name: "T", + TypeBound: sema.NewConjunctionTypeBound( + []sema.TypeBound{ + sema.NewStrictSubtypeTypeBound(sema.FixedSizeUnsignedIntegerType), + sema.NewStrictSupertypeTypeBound(sema.NeverType), + }, + ), } typeAnnotation := sema.NewTypeAnnotation( @@ -64,29 +65,6 @@ var revertibleRandomFunctionType = func() *sema.FunctionType { TypeAnnotation: typeAnnotation, }, }, - TypeArgumentsCheck: func( - memoryGauge common.MemoryGauge, - typeArguments *sema.TypeParameterTypeOrderedMap, - _ []*ast.TypeAnnotation, - invocationRange ast.HasPosition, - report func(err error)) { - typeArg, ok := typeArguments.Get(typeParameter) - if !ok || typeArg == nil { - // checker should prevent this - panic(errors.NewUnreachableError()) - } - if typeArg == sema.NeverType || typeArg == sema.FixedSizeUnsignedIntegerType { - report(&sema.InvalidTypeArgumentError{ - TypeArgumentName: typeParameter.Name, - Range: ast.NewRangeFromPositioned(memoryGauge, invocationRange), - Details: fmt.Sprintf( - "Type argument for `%s` cannot be `%s`", - revertibleRandomFunctionName, - typeArg, - ), - }) - } - }, ReturnTypeAnnotation: typeAnnotation, // `modulo` parameter is optional Arity: &sema.Arity{Min: 0, Max: 1}, diff --git a/runtime/tests/checker/account_test.go b/runtime/tests/checker/account_test.go index b80523d461..db60d475af 100644 --- a/runtime/tests/checker/account_test.go +++ b/runtime/tests/checker/account_test.go @@ -1739,8 +1739,8 @@ func TestCheckAccountCapabilities(t *testing.T) { `) errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) - require.Equal(t, errs[0].(errors.SecondaryError).SecondaryError(), "Type argument for `get` cannot be `Never`") + require.IsType(t, &sema.TypeBoundError{}, errs[0]) + require.Equal(t, errs[0].(errors.SecondaryError).SecondaryError(), "expected type satisfying {[{&Any} {{Never}}]}, got `Never`") }) } diff --git a/runtime/tests/checker/builtinfunctions_test.go b/runtime/tests/checker/builtinfunctions_test.go index bbb41882b0..4d2454603b 100644 --- a/runtime/tests/checker/builtinfunctions_test.go +++ b/runtime/tests/checker/builtinfunctions_test.go @@ -406,8 +406,8 @@ func TestCheckRevertibleRandom(t *testing.T) { "invalid type arg never", `let rand = revertibleRandom(modulo: 1)`, []error{ + &sema.TypeBoundError{}, &sema.TypeMismatchError{}, - &sema.InvalidTypeArgumentError{}, }, ) runInvalidCase( @@ -415,7 +415,7 @@ func TestCheckRevertibleRandom(t *testing.T) { "invalid type arg FixedSizeUnsignedInteger", `let rand = revertibleRandom(modulo: 1)`, []error{ - &sema.InvalidTypeArgumentError{}, + &sema.TypeBoundError{}, }, ) From 539f15f357f39efd3fc3b29c2f124da1f8d0dacf Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 2 Apr 2024 11:18:57 -0400 Subject: [PATCH 04/15] use generalized bounds for inclusive range --- runtime/program_params_validation_test.go | 4 +- runtime/sema/type.go | 24 +++++++---- runtime/sema/typebound.go | 8 +++- runtime/sema/typebound_test.go | 3 +- runtime/stdlib/range.go | 50 ++--------------------- runtime/tests/checker/range_value_test.go | 12 +++--- 6 files changed, 35 insertions(+), 66 deletions(-) diff --git a/runtime/program_params_validation_test.go b/runtime/program_params_validation_test.go index f9a2b693db..d7f202af4b 100644 --- a/runtime/program_params_validation_test.go +++ b/runtime/program_params_validation_test.go @@ -348,7 +348,7 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { require.ErrorAs(t, err, &checkerError) errs := checker.RequireCheckerErrors(t, checkerError, 1) - assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run("Invalid InclusiveRange with mixed value types", func(t *testing.T) { @@ -387,7 +387,7 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { require.ErrorAs(t, err, &checkerError) errs := checker.RequireCheckerErrors(t, checkerError, 1) - assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run("Capability", func(t *testing.T) { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 9430b3d2cb..58b07b3028 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6698,15 +6698,9 @@ func (t *InclusiveRangeType) Instantiate( } paramAstRange := ast.NewRangeFromPositioned(memoryGauge, astTypeArguments[0]) - // memberType must only be a leaf integer type. - for _, ty := range AllNonLeafIntegerTypes { - if memberType == ty { - report(&InvalidTypeArgumentError{ - TypeArgumentName: inclusiveRangeTypeParameter.Name, - Range: paramAstRange, - Details: fmt.Sprintf("Creation of InclusiveRange<%s> is disallowed", memberType), - }) - } + err := InclusiveRangeConstructorFunctionTypeParameter.checkTypeBound(memberType, memoryGauge, paramAstRange) + if err != nil { + report(err) } return &InclusiveRangeType{ @@ -6762,6 +6756,18 @@ const inclusiveRangeTypeContainsFunctionDocString = ` Returns true if the given integer is in the InclusiveRange sequence ` +var InclusiveRangeConstructorFunctionTypeParameter = &TypeParameter{ + Name: "T", + TypeBound: NewDisjunctionTypeBound( + []TypeBound{ + NewEqualTypeBound(UIntType), + NewEqualTypeBound(IntType), + NewStrictSubtypeTypeBound(FixedSizeUnsignedIntegerType), + NewStrictSubtypeTypeBound(SignedIntegerType), + }, + ), +} + func (t *InclusiveRangeType) GetMembers() map[string]MemberResolver { t.initializeMemberResolvers() return t.memberResolvers diff --git a/runtime/sema/typebound.go b/runtime/sema/typebound.go index d6ee4eba68..24600a25e8 100644 --- a/runtime/sema/typebound.go +++ b/runtime/sema/typebound.go @@ -365,7 +365,11 @@ func NewSupertypeTypeBound(ty Type) TypeBound { return NewNegationTypeBound(NewStrictSubtypeTypeBound(ty)) } -// `!(B1 & ... & Bn) ==> B1 || ... || Bn` +// `!(!B1 & ... & !Bn) ==> B1 || ... || Bn` func NewDisjunctionTypeBound(typeBounds []TypeBound) TypeBound { - return NewNegationTypeBound(NewConjunctionTypeBound(typeBounds)) + var negatedTypeBounds []TypeBound + for _, bound := range typeBounds { + negatedTypeBounds = append(negatedTypeBounds, NewNegationTypeBound(bound)) + } + return NewNegationTypeBound(NewConjunctionTypeBound(negatedTypeBounds)) } diff --git a/runtime/sema/typebound_test.go b/runtime/sema/typebound_test.go index 1f63ab52b0..2ee01dfb6a 100644 --- a/runtime/sema/typebound_test.go +++ b/runtime/sema/typebound_test.go @@ -110,7 +110,8 @@ func TestTypeBound_Satisfies(t *testing.T) { }) assert.True(t, typeBound.Satisfies(FixedSizeUnsignedIntegerType)) - assert.True(t, typeBound.Satisfies(NeverType)) + assert.True(t, typeBound.Satisfies(AnyStructType)) + assert.False(t, typeBound.Satisfies(NeverType)) for _, integerType := range AllLeafFixedSizeUnsignedIntegerTypes { assert.True(t, typeBound.Satisfies(integerType)) diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index 9bc99c5555..26cf227981 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -21,8 +21,6 @@ package stdlib import ( "fmt" - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" @@ -37,23 +35,17 @@ const inclusiveRangeConstructorFunctionDocString = ` If not provided, the value of +1 or -1 is used based on the values of start and end. ` -var inclusiveRangeConstructorFunctionType = func() *sema.FunctionType { - typeParameter := &sema.TypeParameter{ - Name: "T", - TypeBound: sema.SubtypeTypeBound{ - Type: sema.IntegerType, - }, - } +var InclusiveRangeConstructorFunctionType = func() *sema.FunctionType { typeAnnotation := sema.NewTypeAnnotation( &sema.GenericType{ - TypeParameter: typeParameter, + TypeParameter: sema.InclusiveRangeConstructorFunctionTypeParameter, }, ) return &sema.FunctionType{ TypeParameters: []*sema.TypeParameter{ - typeParameter, + sema.InclusiveRangeConstructorFunctionTypeParameter, }, Parameters: []sema.Parameter{ { @@ -78,46 +70,12 @@ var inclusiveRangeConstructorFunctionType = func() *sema.FunctionType { ), // `step` parameter is optional Arity: &sema.Arity{Min: 2, Max: 3}, - TypeArgumentsCheck: func( - memoryGauge common.MemoryGauge, - typeArguments *sema.TypeParameterTypeOrderedMap, - astTypeArguments []*ast.TypeAnnotation, - invocationRange ast.HasPosition, - report func(error), - ) { - memberType, ok := typeArguments.Get(typeParameter) - if !ok || memberType == nil { - // checker should prevent this - panic(errors.NewUnreachableError()) - } - - // memberType must only be a leaf integer type. - for _, ty := range sema.AllNonLeafIntegerTypes { - if memberType != ty { - continue - } - - // If type argument was provided, use its range otherwise fallback to invocation range. - errorRange := invocationRange - if len(astTypeArguments) > 0 { - errorRange = astTypeArguments[0] - } - - report(&sema.InvalidTypeArgumentError{ - TypeArgumentName: typeParameter.Name, - Range: ast.NewRangeFromPositioned(memoryGauge, errorRange), - Details: fmt.Sprintf("Creation of InclusiveRange<%s> is disallowed", memberType), - }) - - break - } - }, } }() var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( "InclusiveRange", - inclusiveRangeConstructorFunctionType, + InclusiveRangeConstructorFunctionType, inclusiveRangeConstructorFunctionDocString, func(invocation interpreter.Invocation) interpreter.Value { start, startOk := invocation.Arguments[0].(interpreter.IntegerValue) diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index b5963727a6..ea7f97d94c 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -389,7 +389,7 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { let b: Integer = Int16(10) let r = InclusiveRange(a, b) `, - []error{&sema.InvalidTypeArgumentError{}}, + []error{&sema.TypeBoundError{}}, ) runInvalidCase( t, @@ -400,7 +400,7 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { let s: Integer = UInt16(2) let r = InclusiveRange(a, b, step: s) `, - []error{&sema.InvalidTypeArgumentError{}}, + []error{&sema.TypeBoundError{}}, ) } @@ -432,7 +432,7 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { `, ty), newOptions()) errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) t.Run(fmt.Sprintf("InclusiveRange<%s> infer from lhs", ty), func(t *testing.T) { @@ -446,8 +446,8 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { // One for the invocation and another for the type. errs := RequireCheckerErrors(t, err, 2) - assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) - assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[1]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[1]) }) t.Run(fmt.Sprintf("InclusiveRange<%s> assignment", ty), func(t *testing.T) { @@ -459,7 +459,7 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { `, ty), newOptions()) errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) + assert.IsType(t, &sema.TypeBoundError{}, errs[0]) }) } From 236ba1373536be64e9cf27bd2315bfc500b249e4 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 2 Apr 2024 11:31:42 -0400 Subject: [PATCH 05/15] composition functions for type bounds --- runtime/sema/account.go | 8 ++--- runtime/sema/type.go | 12 +++----- runtime/sema/typebound.go | 65 +++++++++++++++++++++++++++++++++++---- runtime/stdlib/random.go | 8 ++--- 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/runtime/sema/account.go b/runtime/sema/account.go index ec6598ace5..72189f1060 100644 --- a/runtime/sema/account.go +++ b/runtime/sema/account.go @@ -61,12 +61,8 @@ func init() { // TODO: if we add support for arbitrary logical type bounds to the source language, move this // into the generator Account_CapabilitiesTypeGetFunctionType.TypeParameters[0].TypeBound = - NewConjunctionTypeBound( - []TypeBound{ - Account_CapabilitiesTypeGetFunctionType.TypeParameters[0].TypeBound, - NewStrictSupertypeTypeBound(NeverType), - }, - ) + Account_CapabilitiesTypeGetFunctionType.TypeParameters[0].TypeBound. + And(NewStrictSupertypeTypeBound(NeverType)) addToBaseActivation(AccountMappingType) addToBaseActivation(CapabilitiesMappingType) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 58b07b3028..2c899ff22f 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6758,14 +6758,10 @@ Returns true if the given integer is in the InclusiveRange sequence var InclusiveRangeConstructorFunctionTypeParameter = &TypeParameter{ Name: "T", - TypeBound: NewDisjunctionTypeBound( - []TypeBound{ - NewEqualTypeBound(UIntType), - NewEqualTypeBound(IntType), - NewStrictSubtypeTypeBound(FixedSizeUnsignedIntegerType), - NewStrictSubtypeTypeBound(SignedIntegerType), - }, - ), + TypeBound: NewEqualTypeBound(UIntType). + Or(NewEqualTypeBound(IntType)). + Or(NewStrictSubtypeTypeBound(FixedSizeUnsignedIntegerType)). + Or(NewStrictSubtypeTypeBound(SignedIntegerType)), } func (t *InclusiveRangeType) GetMembers() map[string]MemberResolver { diff --git a/runtime/sema/typebound.go b/runtime/sema/typebound.go index 24600a25e8..cb222e8103 100644 --- a/runtime/sema/typebound.go +++ b/runtime/sema/typebound.go @@ -42,6 +42,10 @@ type TypeBound interface { ) TypeBound TypeAnnotationState() TypeAnnotationState RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) + + And(TypeBound) TypeBound + Or(TypeBound) TypeBound + Not() TypeBound } // SubtypeTypeBound(T) expresses the requirement that @@ -107,6 +111,18 @@ func (b SubtypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewr return b, false } +func (b SubtypeTypeBound) And(bound TypeBound) TypeBound { + return NewConjunctionTypeBound([]TypeBound{b, bound}) +} + +func (b SubtypeTypeBound) Or(bound TypeBound) TypeBound { + return NewDisjunctionTypeBound([]TypeBound{b, bound}) +} + +func (b SubtypeTypeBound) Not() TypeBound { + return NewNegationTypeBound(b) +} + // EqualTypeBound expresses the requirement that // ∀U, U = T @@ -170,6 +186,18 @@ func (b EqualTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewrit return b, false } +func (b EqualTypeBound) And(bound TypeBound) TypeBound { + return NewConjunctionTypeBound([]TypeBound{b, bound}) +} + +func (b EqualTypeBound) Or(bound TypeBound) TypeBound { + return NewDisjunctionTypeBound([]TypeBound{b, bound}) +} + +func (b EqualTypeBound) Not() TypeBound { + return NewNegationTypeBound(b) +} + // NegationTypeBound(B) expresses the requirement that // ∀U, !B(U) @@ -233,6 +261,19 @@ func (b NegationTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rew return b, false } +func (b NegationTypeBound) And(bound TypeBound) TypeBound { + return NewConjunctionTypeBound([]TypeBound{b, bound}) +} + +func (b NegationTypeBound) Or(bound TypeBound) TypeBound { + return NewDisjunctionTypeBound([]TypeBound{b, bound}) +} + +func (b NegationTypeBound) Not() TypeBound { + // !!b = b + return b.NegatedBound +} + // ConjunctionTypeBound(B1, ..., Bn) expresses the requirement that // ∀U, B1(U) & ... & Bn(U) @@ -343,6 +384,18 @@ func (b ConjunctionTypeBound) RewriteWithIntersectionTypes() (result TypeBound, } } +func (b ConjunctionTypeBound) And(bound TypeBound) TypeBound { + return NewConjunctionTypeBound(append(b.TypeBounds, bound)) +} + +func (b ConjunctionTypeBound) Or(bound TypeBound) TypeBound { + return NewDisjunctionTypeBound([]TypeBound{b, bound}) +} + +func (b ConjunctionTypeBound) Not() TypeBound { + return NewNegationTypeBound(b) +} + // Any other kinds of type bounds we might wish to express can be // written as the composition of `<=`, `=`, `!` and `&`. Technically, `=` is not // really even necessary, as `U = T` is equivalent to `U <= T & T <= U`, but for @@ -351,25 +404,25 @@ func (b ConjunctionTypeBound) RewriteWithIntersectionTypes() (result TypeBound, // `U <= T && !(T = U) ==> U < T` func NewStrictSubtypeTypeBound(ty Type) TypeBound { subtypeBound := NewSubtypeTypeBound(ty) - nonEqualBound := NewNegationTypeBound(NewEqualTypeBound(ty)) - return NewConjunctionTypeBound([]TypeBound{subtypeBound, nonEqualBound}) + nonEqualBound := NewEqualTypeBound(ty).Not() + return subtypeBound.And(nonEqualBound) } // `!(U <= T) ==> U > T` func NewStrictSupertypeTypeBound(ty Type) TypeBound { - return NewNegationTypeBound(NewSubtypeTypeBound(ty)) + return NewSubtypeTypeBound(ty).Not() } // `!(U < T) ==> U >= T` func NewSupertypeTypeBound(ty Type) TypeBound { - return NewNegationTypeBound(NewStrictSubtypeTypeBound(ty)) + return NewStrictSubtypeTypeBound(ty).Not() } // `!(!B1 & ... & !Bn) ==> B1 || ... || Bn` func NewDisjunctionTypeBound(typeBounds []TypeBound) TypeBound { var negatedTypeBounds []TypeBound for _, bound := range typeBounds { - negatedTypeBounds = append(negatedTypeBounds, NewNegationTypeBound(bound)) + negatedTypeBounds = append(negatedTypeBounds, bound.Not()) } - return NewNegationTypeBound(NewConjunctionTypeBound(negatedTypeBounds)) + return NewConjunctionTypeBound(negatedTypeBounds).Not() } diff --git a/runtime/stdlib/random.go b/runtime/stdlib/random.go index 3020a745c6..a090f2b7eb 100644 --- a/runtime/stdlib/random.go +++ b/runtime/stdlib/random.go @@ -41,12 +41,8 @@ const revertibleRandomFunctionName = "revertibleRandom" var revertibleRandomFunctionType = func() *sema.FunctionType { typeParameter := &sema.TypeParameter{ Name: "T", - TypeBound: sema.NewConjunctionTypeBound( - []sema.TypeBound{ - sema.NewStrictSubtypeTypeBound(sema.FixedSizeUnsignedIntegerType), - sema.NewStrictSupertypeTypeBound(sema.NeverType), - }, - ), + TypeBound: sema.NewStrictSubtypeTypeBound(sema.FixedSizeUnsignedIntegerType). + And(sema.NewStrictSupertypeTypeBound(sema.NeverType)), } typeAnnotation := sema.NewTypeAnnotation( From 142bf814aa62b53046f8b9e2ef9a566c290aa0a4 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 2 Apr 2024 12:31:01 -0400 Subject: [PATCH 06/15] pretty printing for type bounds --- runtime/account_test.go | 2 +- runtime/sema/typebound.go | 110 ++++++++++++++++++++------ runtime/sema/typebound_test.go | 89 ++++++++++++++++++++- runtime/tests/checker/account_test.go | 2 +- 4 files changed, 174 insertions(+), 29 deletions(-) diff --git a/runtime/account_test.go b/runtime/account_test.go index 2330ce4ddc..05f307bfae 100644 --- a/runtime/account_test.go +++ b/runtime/account_test.go @@ -184,7 +184,7 @@ func TestRuntimeStoreAccountAPITypes(t *testing.T) { RequireError(t, err) - assert.Contains(t, err.Error(), "expected type satisfying {Storable}") + assert.Contains(t, err.Error(), "expected type satisfying <=: Storable") } } diff --git a/runtime/sema/typebound.go b/runtime/sema/typebound.go index cb222e8103..b52ea3150c 100644 --- a/runtime/sema/typebound.go +++ b/runtime/sema/typebound.go @@ -1,24 +1,27 @@ /* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +* Cadence - The resource-oriented smart contract programming language +* +* Copyright Dapper Labs, Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. */ package sema import ( + "fmt" + "strings" + "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" ) @@ -46,6 +49,9 @@ type TypeBound interface { And(TypeBound) TypeBound Or(TypeBound) TypeBound Not() TypeBound + + withPrettyString(string) TypeBound + String() string } // SubtypeTypeBound(T) expresses the requirement that @@ -123,6 +129,14 @@ func (b SubtypeTypeBound) Not() TypeBound { return NewNegationTypeBound(b) } +func (b SubtypeTypeBound) String() string { + return fmt.Sprintf("<=: %s", b.Type.String()) +} + +func (b SubtypeTypeBound) withPrettyString(_ string) TypeBound { + return b +} + // EqualTypeBound expresses the requirement that // ∀U, U = T @@ -198,14 +212,23 @@ func (b EqualTypeBound) Not() TypeBound { return NewNegationTypeBound(b) } +func (b EqualTypeBound) String() string { + return fmt.Sprintf("= %s", b.Type.String()) +} + +func (b EqualTypeBound) withPrettyString(_ string) TypeBound { + return b +} + // NegationTypeBound(B) expresses the requirement that // ∀U, !B(U) type NegationTypeBound struct { NegatedBound TypeBound + prettyString *string } -var _ TypeBound = NegationTypeBound{} +var _ TypeBound = &NegationTypeBound{} func NewNegationTypeBound(bound TypeBound) TypeBound { return NegationTypeBound{NegatedBound: bound} @@ -242,7 +265,7 @@ func (b NegationTypeBound) Map( typeParamMap map[*TypeParameter]*TypeParameter, f func(Type) Type, ) TypeBound { - return NegationTypeBound{ + return &NegationTypeBound{ NegatedBound: b.NegatedBound.Map(gauge, typeParamMap, f), } } @@ -254,7 +277,7 @@ func (b NegationTypeBound) TypeAnnotationState() TypeAnnotationState { func (b NegationTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { rewrittenBound, rewritten := b.NegatedBound.RewriteWithIntersectionTypes() if rewritten { - return NegationTypeBound{ + return &NegationTypeBound{ NegatedBound: rewrittenBound, }, true } @@ -274,11 +297,24 @@ func (b NegationTypeBound) Not() TypeBound { return b.NegatedBound } +func (b NegationTypeBound) String() string { + if b.prettyString != nil { + return *b.prettyString + } + return fmt.Sprintf("!(%s)", b.NegatedBound.String()) +} + +func (b NegationTypeBound) withPrettyString(prettyString string) TypeBound { + b.prettyString = &prettyString + return b +} + // ConjunctionTypeBound(B1, ..., Bn) expresses the requirement that // ∀U, B1(U) & ... & Bn(U) type ConjunctionTypeBound struct { - TypeBounds []TypeBound + TypeBounds []TypeBound + prettyString *string } var _ TypeBound = ConjunctionTypeBound{} @@ -396,6 +432,23 @@ func (b ConjunctionTypeBound) Not() TypeBound { return NewNegationTypeBound(b) } +func (b ConjunctionTypeBound) String() string { + if b.prettyString != nil { + return *b.prettyString + } + + var strs []string + for _, bound := range b.TypeBounds { + strs = append(strs, fmt.Sprintf("(%s)", bound.String())) + } + return strings.Join(strs[:], " && ") +} + +func (b ConjunctionTypeBound) withPrettyString(prettyString string) TypeBound { + b.prettyString = &prettyString + return b +} + // Any other kinds of type bounds we might wish to express can be // written as the composition of `<=`, `=`, `!` and `&`. Technically, `=` is not // really even necessary, as `U = T` is equivalent to `U <= T & T <= U`, but for @@ -405,24 +458,35 @@ func (b ConjunctionTypeBound) Not() TypeBound { func NewStrictSubtypeTypeBound(ty Type) TypeBound { subtypeBound := NewSubtypeTypeBound(ty) nonEqualBound := NewEqualTypeBound(ty).Not() - return subtypeBound.And(nonEqualBound) + return subtypeBound. + And(nonEqualBound). + withPrettyString(fmt.Sprintf("<: %s", ty.String())) } // `!(U <= T) ==> U > T` func NewStrictSupertypeTypeBound(ty Type) TypeBound { - return NewSubtypeTypeBound(ty).Not() + return NewSubtypeTypeBound(ty). + Not(). + withPrettyString(fmt.Sprintf(">: %s", ty.String())) } // `!(U < T) ==> U >= T` func NewSupertypeTypeBound(ty Type) TypeBound { - return NewStrictSubtypeTypeBound(ty).Not() + return NewStrictSubtypeTypeBound(ty). + Not(). + withPrettyString(fmt.Sprintf(">=: %s", ty.String())) } // `!(!B1 & ... & !Bn) ==> B1 || ... || Bn` func NewDisjunctionTypeBound(typeBounds []TypeBound) TypeBound { var negatedTypeBounds []TypeBound + var strs []string for _, bound := range typeBounds { negatedTypeBounds = append(negatedTypeBounds, bound.Not()) + strs = append(strs, fmt.Sprintf("(%s)", bound.String())) } - return NewConjunctionTypeBound(negatedTypeBounds).Not() + + return NewConjunctionTypeBound(negatedTypeBounds). + Not(). + withPrettyString(strings.Join(strs[:], " || ")) } diff --git a/runtime/sema/typebound_test.go b/runtime/sema/typebound_test.go index 2ee01dfb6a..eac57753aa 100644 --- a/runtime/sema/typebound_test.go +++ b/runtime/sema/typebound_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestTypeBound_Satisfies(t *testing.T) { @@ -119,6 +120,86 @@ func TestTypeBound_Satisfies(t *testing.T) { }) } +func TestTypeBoundSerialization(t *testing.T) { + t.Parallel() + + type testCase struct { + name string + bound TypeBound + expected string + } + + tests := []testCase{ + { + name: "basic subtype", + bound: NewSubtypeTypeBound(IntType), + expected: "<=: Int", + }, + { + name: "basic equal", + bound: NewEqualTypeBound(IntType), + expected: "= Int", + }, + { + name: "basic not subtype", + bound: NewSubtypeTypeBound(IntType).Not(), + expected: "!(<=: Int)", + }, + { + name: "basic not equal", + bound: NewEqualTypeBound(IntType).Not(), + expected: "!(= Int)", + }, + { + name: "basic conjunction", + bound: NewSubtypeTypeBound(IntType).And(NewEqualTypeBound(StringType)), + expected: "(<=: Int) && (= String)", + }, + { + name: "conjunction of negations", + bound: NewSubtypeTypeBound(IntType).Not().And(NewEqualTypeBound(StringType).Not()), + expected: "(!(<=: Int)) && (!(= String))", + }, + { + name: "flattened double negative", + bound: NewSubtypeTypeBound(IntType).Not().Not(), + expected: "<=: Int", + }, + { + name: "strict subtype", + bound: NewStrictSubtypeTypeBound(IntType), + expected: "<: Int", + }, + { + name: "strict supertype", + bound: NewStrictSupertypeTypeBound(IntType), + expected: ">: Int", + }, + { + name: "supertype", + bound: NewSupertypeTypeBound(IntType), + expected: ">=: Int", + }, + { + name: "basic disjunction", + bound: NewSubtypeTypeBound(IntType).Or(NewEqualTypeBound(StringType)), + expected: "(<=: Int) || (= String)", + }, + } + + test := func(test testCase) { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + require.Equal(t, test.bound.String(), test.expected) + }) + } + + for _, testCase := range tests { + test(testCase) + } +} + func TestTypeBound_HasInvalid(t *testing.T) { t.Parallel() @@ -159,19 +240,19 @@ func TestTypeBound_HasInvalid(t *testing.T) { t.Parallel() assert.False(t, - ConjunctionTypeBound{ + (&ConjunctionTypeBound{ TypeBounds: []TypeBound{ SubtypeTypeBound{Type: IntegerType}, }, - }.HasInvalidType(), + }).HasInvalidType(), ) assert.True(t, - ConjunctionTypeBound{ + (&ConjunctionTypeBound{ TypeBounds: []TypeBound{ SubtypeTypeBound{Type: InvalidType}, }, - }.HasInvalidType(), + }).HasInvalidType(), ) }) } diff --git a/runtime/tests/checker/account_test.go b/runtime/tests/checker/account_test.go index db60d475af..96dd68e037 100644 --- a/runtime/tests/checker/account_test.go +++ b/runtime/tests/checker/account_test.go @@ -1740,7 +1740,7 @@ func TestCheckAccountCapabilities(t *testing.T) { errs := RequireCheckerErrors(t, err, 1) require.IsType(t, &sema.TypeBoundError{}, errs[0]) - require.Equal(t, errs[0].(errors.SecondaryError).SecondaryError(), "expected type satisfying {[{&Any} {{Never}}]}, got `Never`") + require.Equal(t, errs[0].(errors.SecondaryError).SecondaryError(), "expected type satisfying (<=: &Any) && (>: Never), got `Never`") }) } From 39e81582bdb20acc41b35ba3562770794e89d2a1 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 2 Apr 2024 12:41:25 -0400 Subject: [PATCH 07/15] better printing --- runtime/sema/typebound_test.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/runtime/sema/typebound_test.go b/runtime/sema/typebound_test.go index eac57753aa..7295be1c4f 100644 --- a/runtime/sema/typebound_test.go +++ b/runtime/sema/typebound_test.go @@ -185,6 +185,40 @@ func TestTypeBoundSerialization(t *testing.T) { bound: NewSubtypeTypeBound(IntType).Or(NewEqualTypeBound(StringType)), expected: "(<=: Int) || (= String)", }, + { + name: "triple conjunction", + bound: NewSubtypeTypeBound(IntType). + And(NewEqualTypeBound(StringType)). + And(NewStrictSupertypeTypeBound(BoolType)), + expected: "(<=: Int) && (= String) && (>: Bool)", + }, + { + name: "Capabilities.get", + bound: Account_CapabilitiesTypeGetFunctionTypeParameterT.TypeBound, + expected: "(<=: &Any) && (>: Never)", + }, + { + name: "InclusiveRange", + bound: InclusiveRangeConstructorFunctionTypeParameter.TypeBound, + expected: "(((= UInt) || (= Int)) || (<: FixedSizeUnsignedInteger)) || (<: SignedInteger)", + }, + { + name: "triple disjunction", + bound: NewSubtypeTypeBound(IntType). + Or(NewEqualTypeBound(StringType)). + Or(NewStrictSupertypeTypeBound(BoolType)), + expected: "((<=: Int) || (= String)) || (>: Bool)", + }, + { + name: "conjunction of disjunctions", + bound: NewSubtypeTypeBound(IntType). + Or(NewEqualTypeBound(StringType)). + And( + NewStrictSupertypeTypeBound(BoolType). + Or(NewSupertypeTypeBound(AnyStructType)), + ), + expected: "((<=: Int) || (= String)) && ((>: Bool) || (>=: AnyStruct))", + }, } test := func(test testCase) { From 047709c603bc32b5ab9e8ada6824286a937162fa Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 2 Apr 2024 14:00:06 -0400 Subject: [PATCH 08/15] golf --- runtime/sema/typebound.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/runtime/sema/typebound.go b/runtime/sema/typebound.go index b52ea3150c..d71c846193 100644 --- a/runtime/sema/typebound.go +++ b/runtime/sema/typebound.go @@ -456,10 +456,9 @@ func (b ConjunctionTypeBound) withPrettyString(prettyString string) TypeBound { // `U <= T && !(T = U) ==> U < T` func NewStrictSubtypeTypeBound(ty Type) TypeBound { - subtypeBound := NewSubtypeTypeBound(ty) - nonEqualBound := NewEqualTypeBound(ty).Not() - return subtypeBound. - And(nonEqualBound). + return NewEqualTypeBound(ty). + Not(). + And(NewSubtypeTypeBound(ty)). withPrettyString(fmt.Sprintf("<: %s", ty.String())) } From 6ea0b0290cb597d09b2d862602927ae93763bd17 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 2 Apr 2024 14:37:36 -0400 Subject: [PATCH 09/15] nicer printing for inclusive range param --- runtime/sema/type.go | 10 ++++++---- runtime/sema/typebound_test.go | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 2c899ff22f..93a775e2f1 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6758,10 +6758,12 @@ Returns true if the given integer is in the InclusiveRange sequence var InclusiveRangeConstructorFunctionTypeParameter = &TypeParameter{ Name: "T", - TypeBound: NewEqualTypeBound(UIntType). - Or(NewEqualTypeBound(IntType)). - Or(NewStrictSubtypeTypeBound(FixedSizeUnsignedIntegerType)). - Or(NewStrictSubtypeTypeBound(SignedIntegerType)), + TypeBound: NewDisjunctionTypeBound([]TypeBound{ + NewEqualTypeBound(UIntType), + NewEqualTypeBound(IntType), + NewStrictSubtypeTypeBound(FixedSizeUnsignedIntegerType), + NewStrictSubtypeTypeBound(SignedIntegerType), + }), } func (t *InclusiveRangeType) GetMembers() map[string]MemberResolver { diff --git a/runtime/sema/typebound_test.go b/runtime/sema/typebound_test.go index 7295be1c4f..d237b3b1b0 100644 --- a/runtime/sema/typebound_test.go +++ b/runtime/sema/typebound_test.go @@ -200,7 +200,7 @@ func TestTypeBoundSerialization(t *testing.T) { { name: "InclusiveRange", bound: InclusiveRangeConstructorFunctionTypeParameter.TypeBound, - expected: "(((= UInt) || (= Int)) || (<: FixedSizeUnsignedInteger)) || (<: SignedInteger)", + expected: "(= UInt) || (= Int) || (<: FixedSizeUnsignedInteger) || (<: SignedInteger)", }, { name: "triple disjunction", From decdd94a1b16a2aeeacafdad43607bab264f7764 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 3 Apr 2024 14:29:04 -0400 Subject: [PATCH 10/15] encoding and decoding json/types --- encoding/ccf/ccf_test.go | 2 +- encoding/json/decode.go | 34 ++++- encoding/json/encode.go | 51 ++++++- encoding/json/encoding_test.go | 169 +++++++++++++++++++++- runtime/common/memorykind.go | 5 + runtime/common/memorykind_string.go | 214 ++++++++++++++-------------- runtime/common/metering.go | 5 + runtime/convertTypes.go | 41 +++++- types.go | 126 +++++++++++++++- types_test.go | 12 +- 10 files changed, 529 insertions(+), 130 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index cd3b97a32d..b40f8fbe3e 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -10426,7 +10426,7 @@ func TestEncodeType(t *testing.T) { cadence.TypeValue{ StaticType: &cadence.FunctionType{ TypeParameters: []cadence.TypeParameter{ - {Name: "T", TypeBound: cadence.AnyStructType}, + {Name: "T", TypeBound: cadence.NewSubtypeTypeBound(cadence.AnyStructType)}, }, Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType}, diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 6f60ef6e5f..088bfc88db 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -928,13 +928,43 @@ func (d *Decoder) decodeFunction(valueJSON any) cadence.Function { ) } +func (d *Decoder) decodeTypeBound(valueJSON any, results typeDecodingResults) cadence.TypeBound { + obj := toObject(valueJSON) + + kind := obj.Get("kind") + + switch kind { + case "subtype": + ty := obj.Get("type") + decodedType := d.decodeType(ty, results) + return cadence.NewSubtypeTypeBound(decodedType) + case "equal": + ty := obj.Get("type") + decodedType := d.decodeType(ty, results) + return cadence.NewEqualTypeBound(decodedType) + case "negation": + bounds := toSlice(obj.Get("bounds")) + decodedBound := d.decodeTypeBound(bounds[0], results) + return cadence.NewNegationTypeBound(decodedBound) + case "conjunction": + bounds := toSlice(obj.Get("bounds")) + var decodedBounds []cadence.TypeBound + for _, bound := range bounds { + decodedBounds = append(decodedBounds, d.decodeTypeBound(bound, results)) + } + return cadence.NewConjunctionTypeBound(decodedBounds) + } + + panic(errors.NewDefaultUserError("invalid kind in type bound: %s", kind)) +} + func (d *Decoder) decodeTypeParameter(valueJSON any, results typeDecodingResults) cadence.TypeParameter { obj := toObject(valueJSON) // Unmetered because decodeTypeParameter is metered in decodeTypeParameters and called nowhere else typeBoundObj, ok := obj[typeBoundKey] - var typeBound cadence.Type + var typeBound cadence.TypeBound if ok { - typeBound = d.decodeType(typeBoundObj, results) + typeBound = d.decodeTypeBound(typeBoundObj, results) } return cadence.NewTypeParameter( diff --git a/encoding/json/encode.go b/encoding/json/encode.go index f8e9189f9f..8943fa24a7 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -189,8 +189,14 @@ type jsonIntersectionType struct { } type jsonTypeParameter struct { - Name string `json:"name"` - TypeBound jsonValue `json:"typeBound"` + Name string `json:"name"` + TypeBound jsonTypeBound `json:"typeBound"` +} + +type jsonTypeBound struct { + Kind string `json:"kind"` + Type jsonValue `json:"type"` + Bounds []jsonTypeBound `json:"bounds"` } type jsonParameterType struct { @@ -710,11 +716,48 @@ func preparePath(x cadence.Path) jsonValue { } } +func prepareTypeBound(typeBound cadence.TypeBound, results TypePreparationResults) jsonTypeBound { + switch bound := typeBound.(type) { + case cadence.SubtypeTypeBound: + preparedType := PrepareType(bound.Type, results) + return jsonTypeBound{ + Kind: "subtype", + Type: preparedType, + } + case cadence.EqualTypeBound: + preparedType := PrepareType(bound.Type, results) + return jsonTypeBound{ + Kind: "equal", + Type: preparedType, + } + case cadence.NegationTypeBound: + preparedBound := prepareTypeBound(bound.NegatedBound, results) + return jsonTypeBound{ + Kind: "negation", + Bounds: []jsonTypeBound{preparedBound}, + } + case cadence.ConjunctionTypeBound: + var preparedBounds []jsonTypeBound + + for _, typeBound := range bound.TypeBounds { + preparedBounds = append(preparedBounds, prepareTypeBound(typeBound, results)) + + } + + return jsonTypeBound{ + Kind: "conjunction", + Bounds: preparedBounds, + } + } + + panic(fmt.Errorf("failed to prepare type: unsupported type bound: %T", typeBound)) +} + func prepareTypeParameter(typeParameter cadence.TypeParameter, results TypePreparationResults) jsonTypeParameter { typeBound := typeParameter.TypeBound - var preparedTypeBound jsonValue + var preparedTypeBound jsonTypeBound if typeBound != nil { - preparedTypeBound = PrepareType(typeBound, results) + preparedTypeBound = prepareTypeBound(typeBound, results) } return jsonTypeParameter{ Name: typeParameter.Name, diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 0e35f762b8..4b7719ced7 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -2557,7 +2557,7 @@ func TestEncodeType(t *testing.T) { cadence.TypeValue{ StaticType: &cadence.FunctionType{ TypeParameters: []cadence.TypeParameter{ - {Name: "T", TypeBound: cadence.AnyStructType}, + {Name: "T", TypeBound: cadence.NewSubtypeTypeBound(cadence.AnyStructType)}, }, Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType}, @@ -2581,8 +2581,12 @@ func TestEncodeType(t *testing.T) { { "name": "T", "typeBound": { - "kind": "AnyStruct" - } + "kind": "subtype", + "type": { + "kind": "AnyStruct" + }, + "bounds": null + } } ], "parameters": [ @@ -2602,6 +2606,165 @@ func TestEncodeType(t *testing.T) { }) + t.Run("with equal type bound", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T", TypeBound: cadence.NewEqualTypeBound(cadence.AnyStructType)}, + }, + Parameters: []cadence.Parameter{}, + ReturnType: cadence.IntType, + }, + }, + // language=json + ` + { + "type": "Type", + "value": { + "staticType": { + "kind": "Function", + "purity": "", + "typeID": "fun():Int", + "return": { + "kind": "Int" + }, + "typeParameters": [ + { + "name": "T", + "typeBound": { + "kind": "equal", + "type": { + "kind": "AnyStruct" + }, + "bounds": null + } + } + ], + "parameters": [] + } + } + } + `, + ) + + }) + + t.Run("with negated type bound", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T", TypeBound: cadence.NewNegationTypeBound(cadence.NewEqualTypeBound(cadence.AnyStructType))}, + }, + Parameters: []cadence.Parameter{}, + ReturnType: cadence.IntType, + }, + }, + // language=json + ` + { + "type": "Type", + "value": { + "staticType": { + "kind": "Function", + "purity": "", + "typeID": "fun():Int", + "return": { + "kind": "Int" + }, + "typeParameters": [ + { + "name": "T", + "typeBound": { + "kind": "negation", + "type": null, + "bounds": [ + { + "kind": "equal", + "type": { + "kind": "AnyStruct" + }, + "bounds": null + } + ] + } + } + ], + "parameters": [] + } + } + } + `, + ) + }) + + t.Run("with conjunction type bound", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T", TypeBound: cadence.NewConjunctionTypeBound([]cadence.TypeBound{ + cadence.NewEqualTypeBound(cadence.AnyStructType), + cadence.NewSubtypeTypeBound(cadence.AnyResourceType), + })}, + }, + Parameters: []cadence.Parameter{}, + ReturnType: cadence.IntType, + }, + }, + // language=json + ` + { + "type": "Type", + "value": { + "staticType": { + "kind": "Function", + "purity": "", + "typeID": "fun():Int", + "return": { + "kind": "Int" + }, + "typeParameters": [ + { + "name": "T", + "typeBound": { + "kind": "conjunction", + "type": null, + "bounds": [ + { + "kind": "equal", + "type": { + "kind": "AnyStruct" + }, + "bounds": null + }, + { + "kind": "subtype", + "type": { + "kind": "AnyResource" + }, + "bounds": null + } + ] + } + } + ], + "parameters": [] + } + } + } + `, + ) + + }) + t.Run("with view static function", func(t *testing.T) { testEncodeAndDecode( diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index c3cdf5fc30..0a2badeca6 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -133,6 +133,11 @@ const ( MemoryKindCadenceCapabilityType MemoryKindCadenceEnumType + MemoryKindCadenceSubtypeBound + MemoryKindCadenceEqualBound + MemoryKindCadenceNegationBound + MemoryKindCadenceConjunctionBound + // Misc MemoryKindRawString diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 65ac875cbe..bd9891f4b4 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -105,114 +105,118 @@ func _() { _ = x[MemoryKindCadenceIntersectionType-94] _ = x[MemoryKindCadenceCapabilityType-95] _ = x[MemoryKindCadenceEnumType-96] - _ = x[MemoryKindRawString-97] - _ = x[MemoryKindAddressLocation-98] - _ = x[MemoryKindBytes-99] - _ = x[MemoryKindVariable-100] - _ = x[MemoryKindCompositeTypeInfo-101] - _ = x[MemoryKindCompositeField-102] - _ = x[MemoryKindInvocation-103] - _ = x[MemoryKindStorageMap-104] - _ = x[MemoryKindStorageKey-105] - _ = x[MemoryKindTypeToken-106] - _ = x[MemoryKindErrorToken-107] - _ = x[MemoryKindSpaceToken-108] - _ = x[MemoryKindProgram-109] - _ = x[MemoryKindIdentifier-110] - _ = x[MemoryKindArgument-111] - _ = x[MemoryKindBlock-112] - _ = x[MemoryKindFunctionBlock-113] - _ = x[MemoryKindParameter-114] - _ = x[MemoryKindParameterList-115] - _ = x[MemoryKindTypeParameter-116] - _ = x[MemoryKindTypeParameterList-117] - _ = x[MemoryKindTransfer-118] - _ = x[MemoryKindMembers-119] - _ = x[MemoryKindTypeAnnotation-120] - _ = x[MemoryKindDictionaryEntry-121] - _ = x[MemoryKindFunctionDeclaration-122] - _ = x[MemoryKindCompositeDeclaration-123] - _ = x[MemoryKindAttachmentDeclaration-124] - _ = x[MemoryKindInterfaceDeclaration-125] - _ = x[MemoryKindEntitlementDeclaration-126] - _ = x[MemoryKindEntitlementMappingElement-127] - _ = x[MemoryKindEntitlementMappingDeclaration-128] - _ = x[MemoryKindEnumCaseDeclaration-129] - _ = x[MemoryKindFieldDeclaration-130] - _ = x[MemoryKindTransactionDeclaration-131] - _ = x[MemoryKindImportDeclaration-132] - _ = x[MemoryKindVariableDeclaration-133] - _ = x[MemoryKindSpecialFunctionDeclaration-134] - _ = x[MemoryKindPragmaDeclaration-135] - _ = x[MemoryKindAssignmentStatement-136] - _ = x[MemoryKindBreakStatement-137] - _ = x[MemoryKindContinueStatement-138] - _ = x[MemoryKindEmitStatement-139] - _ = x[MemoryKindExpressionStatement-140] - _ = x[MemoryKindForStatement-141] - _ = x[MemoryKindIfStatement-142] - _ = x[MemoryKindReturnStatement-143] - _ = x[MemoryKindSwapStatement-144] - _ = x[MemoryKindSwitchStatement-145] - _ = x[MemoryKindWhileStatement-146] - _ = x[MemoryKindRemoveStatement-147] - _ = x[MemoryKindBooleanExpression-148] - _ = x[MemoryKindVoidExpression-149] - _ = x[MemoryKindNilExpression-150] - _ = x[MemoryKindStringExpression-151] - _ = x[MemoryKindIntegerExpression-152] - _ = x[MemoryKindFixedPointExpression-153] - _ = x[MemoryKindArrayExpression-154] - _ = x[MemoryKindDictionaryExpression-155] - _ = x[MemoryKindIdentifierExpression-156] - _ = x[MemoryKindInvocationExpression-157] - _ = x[MemoryKindMemberExpression-158] - _ = x[MemoryKindIndexExpression-159] - _ = x[MemoryKindConditionalExpression-160] - _ = x[MemoryKindUnaryExpression-161] - _ = x[MemoryKindBinaryExpression-162] - _ = x[MemoryKindFunctionExpression-163] - _ = x[MemoryKindCastingExpression-164] - _ = x[MemoryKindCreateExpression-165] - _ = x[MemoryKindDestroyExpression-166] - _ = x[MemoryKindReferenceExpression-167] - _ = x[MemoryKindForceExpression-168] - _ = x[MemoryKindPathExpression-169] - _ = x[MemoryKindAttachExpression-170] - _ = x[MemoryKindConstantSizedType-171] - _ = x[MemoryKindDictionaryType-172] - _ = x[MemoryKindFunctionType-173] - _ = x[MemoryKindInstantiationType-174] - _ = x[MemoryKindNominalType-175] - _ = x[MemoryKindOptionalType-176] - _ = x[MemoryKindReferenceType-177] - _ = x[MemoryKindIntersectionType-178] - _ = x[MemoryKindVariableSizedType-179] - _ = x[MemoryKindPosition-180] - _ = x[MemoryKindRange-181] - _ = x[MemoryKindElaboration-182] - _ = x[MemoryKindActivation-183] - _ = x[MemoryKindActivationEntries-184] - _ = x[MemoryKindVariableSizedSemaType-185] - _ = x[MemoryKindConstantSizedSemaType-186] - _ = x[MemoryKindDictionarySemaType-187] - _ = x[MemoryKindOptionalSemaType-188] - _ = x[MemoryKindIntersectionSemaType-189] - _ = x[MemoryKindReferenceSemaType-190] - _ = x[MemoryKindEntitlementSemaType-191] - _ = x[MemoryKindEntitlementMapSemaType-192] - _ = x[MemoryKindEntitlementRelationSemaType-193] - _ = x[MemoryKindCapabilitySemaType-194] - _ = x[MemoryKindInclusiveRangeSemaType-195] - _ = x[MemoryKindOrderedMap-196] - _ = x[MemoryKindOrderedMapEntryList-197] - _ = x[MemoryKindOrderedMapEntry-198] - _ = x[MemoryKindLast-199] + _ = x[MemoryKindCadenceSubtypeBound-97] + _ = x[MemoryKindCadenceEqualBound-98] + _ = x[MemoryKindCadenceNegationBound-99] + _ = x[MemoryKindCadenceConjunctionBound-100] + _ = x[MemoryKindRawString-101] + _ = x[MemoryKindAddressLocation-102] + _ = x[MemoryKindBytes-103] + _ = x[MemoryKindVariable-104] + _ = x[MemoryKindCompositeTypeInfo-105] + _ = x[MemoryKindCompositeField-106] + _ = x[MemoryKindInvocation-107] + _ = x[MemoryKindStorageMap-108] + _ = x[MemoryKindStorageKey-109] + _ = x[MemoryKindTypeToken-110] + _ = x[MemoryKindErrorToken-111] + _ = x[MemoryKindSpaceToken-112] + _ = x[MemoryKindProgram-113] + _ = x[MemoryKindIdentifier-114] + _ = x[MemoryKindArgument-115] + _ = x[MemoryKindBlock-116] + _ = x[MemoryKindFunctionBlock-117] + _ = x[MemoryKindParameter-118] + _ = x[MemoryKindParameterList-119] + _ = x[MemoryKindTypeParameter-120] + _ = x[MemoryKindTypeParameterList-121] + _ = x[MemoryKindTransfer-122] + _ = x[MemoryKindMembers-123] + _ = x[MemoryKindTypeAnnotation-124] + _ = x[MemoryKindDictionaryEntry-125] + _ = x[MemoryKindFunctionDeclaration-126] + _ = x[MemoryKindCompositeDeclaration-127] + _ = x[MemoryKindAttachmentDeclaration-128] + _ = x[MemoryKindInterfaceDeclaration-129] + _ = x[MemoryKindEntitlementDeclaration-130] + _ = x[MemoryKindEntitlementMappingElement-131] + _ = x[MemoryKindEntitlementMappingDeclaration-132] + _ = x[MemoryKindEnumCaseDeclaration-133] + _ = x[MemoryKindFieldDeclaration-134] + _ = x[MemoryKindTransactionDeclaration-135] + _ = x[MemoryKindImportDeclaration-136] + _ = x[MemoryKindVariableDeclaration-137] + _ = x[MemoryKindSpecialFunctionDeclaration-138] + _ = x[MemoryKindPragmaDeclaration-139] + _ = x[MemoryKindAssignmentStatement-140] + _ = x[MemoryKindBreakStatement-141] + _ = x[MemoryKindContinueStatement-142] + _ = x[MemoryKindEmitStatement-143] + _ = x[MemoryKindExpressionStatement-144] + _ = x[MemoryKindForStatement-145] + _ = x[MemoryKindIfStatement-146] + _ = x[MemoryKindReturnStatement-147] + _ = x[MemoryKindSwapStatement-148] + _ = x[MemoryKindSwitchStatement-149] + _ = x[MemoryKindWhileStatement-150] + _ = x[MemoryKindRemoveStatement-151] + _ = x[MemoryKindBooleanExpression-152] + _ = x[MemoryKindVoidExpression-153] + _ = x[MemoryKindNilExpression-154] + _ = x[MemoryKindStringExpression-155] + _ = x[MemoryKindIntegerExpression-156] + _ = x[MemoryKindFixedPointExpression-157] + _ = x[MemoryKindArrayExpression-158] + _ = x[MemoryKindDictionaryExpression-159] + _ = x[MemoryKindIdentifierExpression-160] + _ = x[MemoryKindInvocationExpression-161] + _ = x[MemoryKindMemberExpression-162] + _ = x[MemoryKindIndexExpression-163] + _ = x[MemoryKindConditionalExpression-164] + _ = x[MemoryKindUnaryExpression-165] + _ = x[MemoryKindBinaryExpression-166] + _ = x[MemoryKindFunctionExpression-167] + _ = x[MemoryKindCastingExpression-168] + _ = x[MemoryKindCreateExpression-169] + _ = x[MemoryKindDestroyExpression-170] + _ = x[MemoryKindReferenceExpression-171] + _ = x[MemoryKindForceExpression-172] + _ = x[MemoryKindPathExpression-173] + _ = x[MemoryKindAttachExpression-174] + _ = x[MemoryKindConstantSizedType-175] + _ = x[MemoryKindDictionaryType-176] + _ = x[MemoryKindFunctionType-177] + _ = x[MemoryKindInstantiationType-178] + _ = x[MemoryKindNominalType-179] + _ = x[MemoryKindOptionalType-180] + _ = x[MemoryKindReferenceType-181] + _ = x[MemoryKindIntersectionType-182] + _ = x[MemoryKindVariableSizedType-183] + _ = x[MemoryKindPosition-184] + _ = x[MemoryKindRange-185] + _ = x[MemoryKindElaboration-186] + _ = x[MemoryKindActivation-187] + _ = x[MemoryKindActivationEntries-188] + _ = x[MemoryKindVariableSizedSemaType-189] + _ = x[MemoryKindConstantSizedSemaType-190] + _ = x[MemoryKindDictionarySemaType-191] + _ = x[MemoryKindOptionalSemaType-192] + _ = x[MemoryKindIntersectionSemaType-193] + _ = x[MemoryKindReferenceSemaType-194] + _ = x[MemoryKindEntitlementSemaType-195] + _ = x[MemoryKindEntitlementMapSemaType-196] + _ = x[MemoryKindEntitlementRelationSemaType-197] + _ = x[MemoryKindCapabilitySemaType-198] + _ = x[MemoryKindInclusiveRangeSemaType-199] + _ = x[MemoryKindOrderedMap-200] + _ = x[MemoryKindOrderedMapEntryList-201] + _ = x[MemoryKindOrderedMapEntry-202] + _ = x[MemoryKindLast-203] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueCapabilityValueStorageReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeInclusiveRangeStaticTypeOptionalStaticTypeIntersectionStaticTypeEntitlementSetStaticAccessEntitlementMapStaticAccessReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceInclusiveRangeValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathValueCadenceTypeValueCadenceCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceInclusiveRangeTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceEntitlementSetAccessCadenceEntitlementMapAccessCadenceReferenceTypeCadenceIntersectionTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEntitlementDeclarationEntitlementMappingElementEntitlementMappingDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeIntersectionTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeIntersectionSemaTypeReferenceSemaTypeEntitlementSemaTypeEntitlementMapSemaTypeEntitlementRelationSemaTypeCapabilitySemaTypeInclusiveRangeSemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueCapabilityValueStorageReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeInclusiveRangeStaticTypeOptionalStaticTypeIntersectionStaticTypeEntitlementSetStaticAccessEntitlementMapStaticAccessReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceInclusiveRangeValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathValueCadenceTypeValueCadenceCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceInclusiveRangeTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceEntitlementSetAccessCadenceEntitlementMapAccessCadenceReferenceTypeCadenceIntersectionTypeCadenceCapabilityTypeCadenceEnumTypeCadenceSubtypeBoundCadenceEqualBoundCadenceNegationBoundCadenceConjunctionBoundRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEntitlementDeclarationEntitlementMappingElementEntitlementMappingDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeIntersectionTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeIntersectionSemaTypeReferenceSemaTypeEntitlementSemaTypeEntitlementMapSemaTypeEntitlementRelationSemaTypeCapabilitySemaTypeInclusiveRangeSemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 176, 197, 220, 244, 261, 279, 285, 305, 319, 351, 383, 401, 423, 448, 464, 484, 507, 534, 550, 569, 588, 607, 630, 653, 673, 697, 715, 737, 763, 789, 808, 828, 846, 862, 882, 898, 916, 937, 956, 971, 989, 1010, 1033, 1055, 1081, 1100, 1122, 1144, 1168, 1194, 1218, 1244, 1265, 1286, 1310, 1334, 1354, 1374, 1390, 1406, 1428, 1448, 1467, 1496, 1525, 1546, 1571, 1583, 1599, 1619, 1636, 1655, 1676, 1692, 1711, 1737, 1765, 1793, 1812, 1839, 1866, 1886, 1909, 1930, 1945, 1954, 1969, 1974, 1982, 1999, 2013, 2023, 2033, 2043, 2052, 2062, 2072, 2079, 2089, 2097, 2102, 2115, 2124, 2137, 2150, 2167, 2175, 2182, 2196, 2211, 2230, 2250, 2271, 2291, 2313, 2338, 2367, 2386, 2402, 2424, 2441, 2460, 2486, 2503, 2522, 2536, 2553, 2566, 2585, 2597, 2608, 2623, 2636, 2651, 2665, 2680, 2697, 2711, 2724, 2740, 2757, 2777, 2792, 2812, 2832, 2852, 2868, 2883, 2904, 2919, 2935, 2953, 2970, 2986, 3003, 3022, 3037, 3051, 3067, 3084, 3098, 3110, 3127, 3138, 3150, 3163, 3179, 3196, 3204, 3209, 3220, 3230, 3247, 3268, 3289, 3307, 3323, 3343, 3360, 3379, 3401, 3428, 3446, 3468, 3478, 3497, 3512, 3516} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 176, 197, 220, 244, 261, 279, 285, 305, 319, 351, 383, 401, 423, 448, 464, 484, 507, 534, 550, 569, 588, 607, 630, 653, 673, 697, 715, 737, 763, 789, 808, 828, 846, 862, 882, 898, 916, 937, 956, 971, 989, 1010, 1033, 1055, 1081, 1100, 1122, 1144, 1168, 1194, 1218, 1244, 1265, 1286, 1310, 1334, 1354, 1374, 1390, 1406, 1428, 1448, 1467, 1496, 1525, 1546, 1571, 1583, 1599, 1619, 1636, 1655, 1676, 1692, 1711, 1737, 1765, 1793, 1812, 1839, 1866, 1886, 1909, 1930, 1945, 1964, 1981, 2001, 2024, 2033, 2048, 2053, 2061, 2078, 2092, 2102, 2112, 2122, 2131, 2141, 2151, 2158, 2168, 2176, 2181, 2194, 2203, 2216, 2229, 2246, 2254, 2261, 2275, 2290, 2309, 2329, 2350, 2370, 2392, 2417, 2446, 2465, 2481, 2503, 2520, 2539, 2565, 2582, 2601, 2615, 2632, 2645, 2664, 2676, 2687, 2702, 2715, 2730, 2744, 2759, 2776, 2790, 2803, 2819, 2836, 2856, 2871, 2891, 2911, 2931, 2947, 2962, 2983, 2998, 3014, 3032, 3049, 3065, 3082, 3101, 3116, 3130, 3146, 3163, 3177, 3189, 3206, 3217, 3229, 3242, 3258, 3275, 3283, 3288, 3299, 3309, 3326, 3347, 3368, 3386, 3402, 3422, 3439, 3458, 3480, 3507, 3525, 3547, 3557, 3576, 3591, 3595} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 529a03a7ef..85aaa34987 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -235,6 +235,11 @@ var ( CadenceStructTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceStructType) CadenceAttachmentTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceAttachmentType) + CadenceSubtypeBoundMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceSubtypeBound) + CadenceEqualBoundMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceEqualBound) + CadenceNegationBoundMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceNegationBound) + CadenceConjunctionBoundMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceConjunctionBound) + // Following are the known memory usage amounts for string representation of interpreter values. // Same as `len(format.X)`. However, values are hard-coded to avoid the circular dependency. diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index e6464ebcee..0d7baa786a 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -496,6 +496,38 @@ func exportInclusiveRangeType( ) } +func exportTypeBound( + gauge common.MemoryGauge, + bound sema.TypeBound, + results map[sema.TypeID]cadence.Type, +) cadence.TypeBound { + switch bound := bound.(type) { + case sema.SubtypeTypeBound: + common.UseMemory(gauge, common.CadenceSubtypeBoundMemoryUsage) + ty := ExportMeteredType(gauge, bound.Type, results) + return cadence.NewSubtypeTypeBound(ty) + case sema.EqualTypeBound: + common.UseMemory(gauge, common.CadenceEqualBoundMemoryUsage) + ty := ExportMeteredType(gauge, bound.Type, results) + return cadence.NewEqualTypeBound(ty) + case sema.ConjunctionTypeBound: + common.UseMemory(gauge, common.CadenceNegationBoundMemoryUsage) + + var joined []cadence.TypeBound + for _, typeBound := range bound.TypeBounds { + joined = append(joined, exportTypeBound(gauge, typeBound, results)) + } + return cadence.NewConjunctionTypeBound(joined) + + case sema.NegationTypeBound: + common.UseMemory(gauge, common.CadenceConjunctionBoundMemoryUsage) + negated := exportTypeBound(gauge, bound.NegatedBound, results) + return cadence.NewNegationTypeBound(negated) + } + + panic(errors.NewUnreachableError()) +} + func exportFunctionType( gauge common.MemoryGauge, t *sema.FunctionType, @@ -514,14 +546,9 @@ func exportFunctionType( for i, typeParameter := range t.TypeParameters { typeBound := typeParameter.TypeBound - var convertedParameterTypeBound cadence.Type + var convertedParameterTypeBound cadence.TypeBound if typeBound != nil { - convertedParameterTypeBound = ExportMeteredType( - gauge, - // TODO: - typeBound.(sema.SubtypeTypeBound).Type, - results, - ) + convertedParameterTypeBound = exportTypeBound(gauge, typeBound, results) } // Metered above diff --git a/types.go b/types.go index 9c21f3a37b..d71677c6a2 100644 --- a/types.go +++ b/types.go @@ -21,6 +21,7 @@ package cadence import ( "fmt" "reflect" + "strings" "sync" "github.com/onflow/cadence/runtime/common" @@ -674,12 +675,12 @@ func NewParameter( type TypeParameter struct { Name string - TypeBound Type + TypeBound TypeBound } func NewTypeParameter( name string, - typeBound Type, + typeBound TypeBound, ) TypeParameter { return TypeParameter{ Name: name, @@ -1813,3 +1814,124 @@ func (t *EnumType) Equal(other Type) bool { return t.Location == otherType.Location && t.QualifiedIdentifier == otherType.QualifiedIdentifier } + +type TypeBound interface { + isTypeBound() + ID() string + Equal(other TypeBound) bool +} + +type SubtypeTypeBound struct { + Type Type +} + +var _ TypeBound = SubtypeTypeBound{} + +func NewSubtypeTypeBound(ty Type) SubtypeTypeBound { + return SubtypeTypeBound{Type: ty} +} + +func (SubtypeTypeBound) isTypeBound() {} + +func (b SubtypeTypeBound) ID() string { + return fmt.Sprintf("<=: %s", b.Type.ID()) +} + +func (b SubtypeTypeBound) Equal(other TypeBound) bool { + otherBound, ok := other.(SubtypeTypeBound) + if !ok { + return false + } + + return b.Type.Equal(otherBound.Type) +} + +type EqualTypeBound struct { + Type Type +} + +var _ TypeBound = EqualTypeBound{} + +func NewEqualTypeBound(ty Type) EqualTypeBound { + return EqualTypeBound{Type: ty} +} + +func (EqualTypeBound) isTypeBound() {} + +func (b EqualTypeBound) ID() string { + return fmt.Sprintf("= %s", b.Type.ID()) +} + +func (b EqualTypeBound) Equal(other TypeBound) bool { + otherBound, ok := other.(EqualTypeBound) + if !ok { + return false + } + + return b.Type.Equal(otherBound.Type) +} + +type NegationTypeBound struct { + NegatedBound TypeBound +} + +var _ TypeBound = NegationTypeBound{} + +func NewNegationTypeBound(bound TypeBound) NegationTypeBound { + return NegationTypeBound{NegatedBound: bound} +} + +func (NegationTypeBound) isTypeBound() {} + +func (b NegationTypeBound) ID() string { + return fmt.Sprintf("!(%s)", b.NegatedBound.ID()) +} + +func (b NegationTypeBound) Equal(other TypeBound) bool { + otherBound, ok := other.(NegationTypeBound) + if !ok { + return false + } + + return b.NegatedBound.Equal(otherBound.NegatedBound) +} + +type ConjunctionTypeBound struct { + TypeBounds []TypeBound +} + +var _ TypeBound = ConjunctionTypeBound{} + +func NewConjunctionTypeBound(bounds []TypeBound) ConjunctionTypeBound { + return ConjunctionTypeBound{TypeBounds: bounds} +} + +func (ConjunctionTypeBound) isTypeBound() {} + +func (b ConjunctionTypeBound) ID() string { + var strs []string + for _, bound := range b.TypeBounds { + strs = append(strs, fmt.Sprintf("(%s)", bound.ID())) + } + return strings.Join(strs[:], " && ") +} + +func (b ConjunctionTypeBound) Equal(bound TypeBound) bool { + other, ok := bound.(ConjunctionTypeBound) + if !ok { + return false + } + + if len(b.TypeBounds) != len(other.TypeBounds) { + return false + } + + for i, typeBound := range b.TypeBounds { + otherTypeBound := other.TypeBounds[i] + if !typeBound.Equal(otherTypeBound) { + return false + } + } + + return true +} diff --git a/types_test.go b/types_test.go index 395134f55f..74a6eb6e8b 100644 --- a/types_test.go +++ b/types_test.go @@ -1408,7 +1408,7 @@ func TestTypeEquality(t *testing.T) { TypeParameters: []TypeParameter{ { Name: "T", - TypeBound: AnyStructType, + TypeBound: NewSubtypeTypeBound(AnyStructType), }, }, Parameters: []Parameter{ @@ -1428,7 +1428,7 @@ func TestTypeEquality(t *testing.T) { TypeParameters: []TypeParameter{ { Name: "T", - TypeBound: AnyStructType, + TypeBound: NewSubtypeTypeBound(AnyStructType), }, }, Parameters: []Parameter{ @@ -1461,7 +1461,7 @@ func TestTypeEquality(t *testing.T) { TypeParameters: []TypeParameter{ { Name: "T", - TypeBound: AnyResourceType, + TypeBound: NewSubtypeTypeBound(AnyResourceType), }, }, Parameters: []Parameter{ @@ -1475,7 +1475,7 @@ func TestTypeEquality(t *testing.T) { TypeParameters: []TypeParameter{ { Name: "T", - TypeBound: AnyStructType, + TypeBound: NewSubtypeTypeBound(AnyStructType), }, }, Parameters: []Parameter{ @@ -1495,7 +1495,7 @@ func TestTypeEquality(t *testing.T) { TypeParameters: []TypeParameter{ { Name: "T", - TypeBound: AnyResourceType, + TypeBound: NewSubtypeTypeBound(AnyResourceType), }, }, Parameters: []Parameter{ @@ -1509,7 +1509,7 @@ func TestTypeEquality(t *testing.T) { TypeParameters: []TypeParameter{ { Name: "T", - TypeBound: AnyResourceType, + TypeBound: NewSubtypeTypeBound(AnyResourceType), }, }, Parameters: []Parameter{ From 5f32e10ea27aaa3201f6f8c88056d7ca18f19960 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 3 Apr 2024 14:45:34 -0400 Subject: [PATCH 11/15] basic ccf implementation for subtype bounds --- encoding/ccf/decode.go | 8 +++++++- encoding/ccf/encode.go | 12 +++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index da42ea7df3..582f545c47 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -2182,9 +2182,15 @@ func (d *Decoder) decodeTypeParameterTypeValue(visited *cadenceTypeByCCFTypeID) return cadence.TypeParameter{}, err } + var typeBound cadence.TypeBound + if t != nil { + typeBound = cadence.NewSubtypeTypeBound(t) + } + // Unmetered because decodeTypeParamTypeValue is metered in decodeTypeParamTypeValues and called nowhere else // Type is metered. - return cadence.NewTypeParameter(name, t), nil + // TODO: implement this for generalized bounds + return cadence.NewTypeParameter(name, typeBound), nil } // decodeParameterTypeValues decodes composite initializer parameter types as diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index e9d755dd3d..0d4b55deef 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1945,8 +1945,18 @@ func (e *Encoder) encodeTypeParameterTypeValue(typeParameter cadence.TypeParamet return err } + // TODO: perhaps generalize this? + if typeParameter.TypeBound == nil { + return e.encodeNullableTypeValue(nil, visited) + } + + subTypeBound, isSubtypeBound := typeParameter.TypeBound.(cadence.SubtypeTypeBound) + if !isSubtypeBound { + panic(cadenceErrors.NewUnexpectedError("cannot store non-subtype bounded function type parameter %s", typeParameter.TypeBound)) + } + // element 1: type as type-bound. - return e.encodeNullableTypeValue(typeParameter.TypeBound, visited) + return e.encodeNullableTypeValue(subTypeBound.Type, visited) } // encodeParameterTypeValues encodes composite initializer parameter types as From 2259e0c3c4a41c8a65efaadf0b3fcbb5745beba8 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 3 Apr 2024 15:19:31 -0400 Subject: [PATCH 12/15] printing for type params --- runtime/sema/type.go | 24 +++++++++++------------- runtime/sema/typebound.go | 4 ++-- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 93a775e2f1..fa5a297ac9 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3398,27 +3398,25 @@ type TypeParameter struct { Optional bool } -func (p TypeParameter) string(typeFormatter func(Type) string) string { +func (p TypeParameter) String() string { var builder strings.Builder builder.WriteString(p.Name) if p.TypeBound != nil { - builder.WriteString(": ") - // TODO: - builder.WriteString(typeFormatter(p.TypeBound.(SubtypeTypeBound).Type)) + // so as not to be confusing, existing subtype bounds will continue to be printed the normal way, + // and we only use special printing for the cases where there are more general bounds + if subtypeBound, isSubtypeBound := p.TypeBound.(SubtypeTypeBound); isSubtypeBound { + builder.WriteString(": ") + builder.WriteString(subtypeBound.Type.String()) + } else { + builder.WriteString(" ") + builder.WriteString(p.TypeBound.String()) + } } return builder.String() } -func (p TypeParameter) String() string { - return p.string(func(t Type) string { - return t.String() - }) -} - func (p TypeParameter) QualifiedString() string { - return p.string(func(t Type) string { - return t.QualifiedString() - }) + return p.String() } func (p TypeParameter) Equal(other *TypeParameter) bool { diff --git a/runtime/sema/typebound.go b/runtime/sema/typebound.go index d71c846193..49f715c1ad 100644 --- a/runtime/sema/typebound.go +++ b/runtime/sema/typebound.go @@ -130,7 +130,7 @@ func (b SubtypeTypeBound) Not() TypeBound { } func (b SubtypeTypeBound) String() string { - return fmt.Sprintf("<=: %s", b.Type.String()) + return fmt.Sprintf("<=: %s", b.Type.QualifiedString()) } func (b SubtypeTypeBound) withPrettyString(_ string) TypeBound { @@ -213,7 +213,7 @@ func (b EqualTypeBound) Not() TypeBound { } func (b EqualTypeBound) String() string { - return fmt.Sprintf("= %s", b.Type.String()) + return fmt.Sprintf("= %s", b.Type.QualifiedString()) } func (b EqualTypeBound) withPrettyString(_ string) TypeBound { From 0a62edcea31c6974d36cb3f4cf15f09a6ea52824 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 3 Apr 2024 15:35:37 -0400 Subject: [PATCH 13/15] add tests for partially ordered types --- runtime/sema/typebound_test.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/runtime/sema/typebound_test.go b/runtime/sema/typebound_test.go index d237b3b1b0..1624156b78 100644 --- a/runtime/sema/typebound_test.go +++ b/runtime/sema/typebound_test.go @@ -36,6 +36,7 @@ func TestTypeBound_Satisfies(t *testing.T) { assert.True(t, typeBound.Satisfies(IntegerType)) assert.True(t, typeBound.Satisfies(NeverType)) + assert.False(t, typeBound.Satisfies(StringType)) for _, integerType := range AllLeafIntegerTypes { assert.True(t, typeBound.Satisfies(integerType)) @@ -50,6 +51,7 @@ func TestTypeBound_Satisfies(t *testing.T) { assert.False(t, typeBound.Satisfies(IntegerType)) assert.True(t, typeBound.Satisfies(NeverType)) + assert.False(t, typeBound.Satisfies(StringType)) for _, integerType := range AllLeafIntegerTypes { assert.Truef(t, typeBound.Satisfies(integerType), "%s should satisfy", integerType) @@ -61,9 +63,11 @@ func TestTypeBound_Satisfies(t *testing.T) { t.Parallel() typeBound := NewSupertypeTypeBound(NeverType) + intTypeBound := NewStrictSupertypeTypeBound(IntType) assert.True(t, typeBound.Satisfies(NeverType)) assert.True(t, typeBound.Satisfies(IntegerType)) + assert.False(t, intTypeBound.Satisfies(StringType)) for _, integerType := range AllLeafIntegerTypes { assert.True(t, typeBound.Satisfies(integerType)) @@ -75,9 +79,11 @@ func TestTypeBound_Satisfies(t *testing.T) { t.Parallel() typeBound := NewStrictSupertypeTypeBound(NeverType) + intTypeBound := NewStrictSupertypeTypeBound(IntType) assert.False(t, typeBound.Satisfies(NeverType)) assert.True(t, typeBound.Satisfies(IntegerType)) + assert.False(t, intTypeBound.Satisfies(StringType)) for _, integerType := range AllLeafIntegerTypes { assert.True(t, typeBound.Satisfies(integerType)) @@ -95,13 +101,14 @@ func TestTypeBound_Satisfies(t *testing.T) { assert.False(t, typeBound.Satisfies(FixedSizeUnsignedIntegerType)) assert.False(t, typeBound.Satisfies(NeverType)) + assert.False(t, typeBound.Satisfies(StringType)) for _, integerType := range AllLeafFixedSizeUnsignedIntegerTypes { assert.True(t, typeBound.Satisfies(integerType)) } }) - t.Run("disjunction", func(t *testing.T) { + t.Run("vacuous disjunction", func(t *testing.T) { t.Parallel() @@ -112,12 +119,29 @@ func TestTypeBound_Satisfies(t *testing.T) { assert.True(t, typeBound.Satisfies(FixedSizeUnsignedIntegerType)) assert.True(t, typeBound.Satisfies(AnyStructType)) + assert.True(t, typeBound.Satisfies(StringType)) assert.False(t, typeBound.Satisfies(NeverType)) for _, integerType := range AllLeafFixedSizeUnsignedIntegerTypes { assert.True(t, typeBound.Satisfies(integerType)) } }) + + t.Run("disjunction", func(t *testing.T) { + + t.Parallel() + + typeBound := NewDisjunctionTypeBound([]TypeBound{ + NewStrictSupertypeTypeBound(StringType), + NewSupertypeTypeBound(IntType), + }) + + assert.True(t, typeBound.Satisfies(FixedSizeUnsignedIntegerType)) + assert.True(t, typeBound.Satisfies(AnyStructType)) + assert.True(t, typeBound.Satisfies(IntType)) + assert.False(t, typeBound.Satisfies(StringType)) + assert.False(t, typeBound.Satisfies(NeverType)) + }) } func TestTypeBoundSerialization(t *testing.T) { From c9f25daf9ce3a3058e3a995c1315bdc9faa95ff8 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 3 Apr 2024 15:50:35 -0400 Subject: [PATCH 14/15] add supertype type bound because our types aren't a total order --- encoding/json/decode.go | 4 + encoding/json/encode.go | 6 + encoding/json/encoding_test.go | 46 ++++++ runtime/common/memorykind.go | 1 + runtime/common/memorykind_string.go | 217 ++++++++++++++-------------- runtime/common/metering.go | 1 + runtime/convertTypes.go | 4 + runtime/sema/typebound.go | 103 +++++++++++-- runtime/sema/typebound_test.go | 3 +- types.go | 25 ++++ 10 files changed, 288 insertions(+), 122 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 088bfc88db..5afed8a7a9 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -938,6 +938,10 @@ func (d *Decoder) decodeTypeBound(valueJSON any, results typeDecodingResults) ca ty := obj.Get("type") decodedType := d.decodeType(ty, results) return cadence.NewSubtypeTypeBound(decodedType) + case "supertype": + ty := obj.Get("type") + decodedType := d.decodeType(ty, results) + return cadence.NewSupertypeTypeBound(decodedType) case "equal": ty := obj.Get("type") decodedType := d.decodeType(ty, results) diff --git a/encoding/json/encode.go b/encoding/json/encode.go index 8943fa24a7..708daee489 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -730,6 +730,12 @@ func prepareTypeBound(typeBound cadence.TypeBound, results TypePreparationResult Kind: "equal", Type: preparedType, } + case cadence.SupertypeTypeBound: + preparedType := PrepareType(bound.Type, results) + return jsonTypeBound{ + Kind: "supertype", + Type: preparedType, + } case cadence.NegationTypeBound: preparedBound := prepareTypeBound(bound.NegatedBound, results) return jsonTypeBound{ diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 4b7719ced7..ad6d64b762 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -2652,6 +2652,52 @@ func TestEncodeType(t *testing.T) { }) + t.Run("with supertype type bound", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T", TypeBound: cadence.NewSupertypeTypeBound(cadence.AnyStructType)}, + }, + Parameters: []cadence.Parameter{}, + ReturnType: cadence.IntType, + }, + }, + // language=json + ` + { + "type": "Type", + "value": { + "staticType": { + "kind": "Function", + "purity": "", + "typeID": "fun():Int", + "return": { + "kind": "Int" + }, + "typeParameters": [ + { + "name": "T", + "typeBound": { + "kind": "supertype", + "type": { + "kind": "AnyStruct" + }, + "bounds": null + } + } + ], + "parameters": [] + } + } + } + `, + ) + + }) + t.Run("with negated type bound", func(t *testing.T) { testEncodeAndDecode( diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 0a2badeca6..71367ecb35 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -134,6 +134,7 @@ const ( MemoryKindCadenceEnumType MemoryKindCadenceSubtypeBound + MemoryKindCadenceSupertypeBound MemoryKindCadenceEqualBound MemoryKindCadenceNegationBound MemoryKindCadenceConjunctionBound diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index bd9891f4b4..5b3c709560 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -106,117 +106,118 @@ func _() { _ = x[MemoryKindCadenceCapabilityType-95] _ = x[MemoryKindCadenceEnumType-96] _ = x[MemoryKindCadenceSubtypeBound-97] - _ = x[MemoryKindCadenceEqualBound-98] - _ = x[MemoryKindCadenceNegationBound-99] - _ = x[MemoryKindCadenceConjunctionBound-100] - _ = x[MemoryKindRawString-101] - _ = x[MemoryKindAddressLocation-102] - _ = x[MemoryKindBytes-103] - _ = x[MemoryKindVariable-104] - _ = x[MemoryKindCompositeTypeInfo-105] - _ = x[MemoryKindCompositeField-106] - _ = x[MemoryKindInvocation-107] - _ = x[MemoryKindStorageMap-108] - _ = x[MemoryKindStorageKey-109] - _ = x[MemoryKindTypeToken-110] - _ = x[MemoryKindErrorToken-111] - _ = x[MemoryKindSpaceToken-112] - _ = x[MemoryKindProgram-113] - _ = x[MemoryKindIdentifier-114] - _ = x[MemoryKindArgument-115] - _ = x[MemoryKindBlock-116] - _ = x[MemoryKindFunctionBlock-117] - _ = x[MemoryKindParameter-118] - _ = x[MemoryKindParameterList-119] - _ = x[MemoryKindTypeParameter-120] - _ = x[MemoryKindTypeParameterList-121] - _ = x[MemoryKindTransfer-122] - _ = x[MemoryKindMembers-123] - _ = x[MemoryKindTypeAnnotation-124] - _ = x[MemoryKindDictionaryEntry-125] - _ = x[MemoryKindFunctionDeclaration-126] - _ = x[MemoryKindCompositeDeclaration-127] - _ = x[MemoryKindAttachmentDeclaration-128] - _ = x[MemoryKindInterfaceDeclaration-129] - _ = x[MemoryKindEntitlementDeclaration-130] - _ = x[MemoryKindEntitlementMappingElement-131] - _ = x[MemoryKindEntitlementMappingDeclaration-132] - _ = x[MemoryKindEnumCaseDeclaration-133] - _ = x[MemoryKindFieldDeclaration-134] - _ = x[MemoryKindTransactionDeclaration-135] - _ = x[MemoryKindImportDeclaration-136] - _ = x[MemoryKindVariableDeclaration-137] - _ = x[MemoryKindSpecialFunctionDeclaration-138] - _ = x[MemoryKindPragmaDeclaration-139] - _ = x[MemoryKindAssignmentStatement-140] - _ = x[MemoryKindBreakStatement-141] - _ = x[MemoryKindContinueStatement-142] - _ = x[MemoryKindEmitStatement-143] - _ = x[MemoryKindExpressionStatement-144] - _ = x[MemoryKindForStatement-145] - _ = x[MemoryKindIfStatement-146] - _ = x[MemoryKindReturnStatement-147] - _ = x[MemoryKindSwapStatement-148] - _ = x[MemoryKindSwitchStatement-149] - _ = x[MemoryKindWhileStatement-150] - _ = x[MemoryKindRemoveStatement-151] - _ = x[MemoryKindBooleanExpression-152] - _ = x[MemoryKindVoidExpression-153] - _ = x[MemoryKindNilExpression-154] - _ = x[MemoryKindStringExpression-155] - _ = x[MemoryKindIntegerExpression-156] - _ = x[MemoryKindFixedPointExpression-157] - _ = x[MemoryKindArrayExpression-158] - _ = x[MemoryKindDictionaryExpression-159] - _ = x[MemoryKindIdentifierExpression-160] - _ = x[MemoryKindInvocationExpression-161] - _ = x[MemoryKindMemberExpression-162] - _ = x[MemoryKindIndexExpression-163] - _ = x[MemoryKindConditionalExpression-164] - _ = x[MemoryKindUnaryExpression-165] - _ = x[MemoryKindBinaryExpression-166] - _ = x[MemoryKindFunctionExpression-167] - _ = x[MemoryKindCastingExpression-168] - _ = x[MemoryKindCreateExpression-169] - _ = x[MemoryKindDestroyExpression-170] - _ = x[MemoryKindReferenceExpression-171] - _ = x[MemoryKindForceExpression-172] - _ = x[MemoryKindPathExpression-173] - _ = x[MemoryKindAttachExpression-174] - _ = x[MemoryKindConstantSizedType-175] - _ = x[MemoryKindDictionaryType-176] - _ = x[MemoryKindFunctionType-177] - _ = x[MemoryKindInstantiationType-178] - _ = x[MemoryKindNominalType-179] - _ = x[MemoryKindOptionalType-180] - _ = x[MemoryKindReferenceType-181] - _ = x[MemoryKindIntersectionType-182] - _ = x[MemoryKindVariableSizedType-183] - _ = x[MemoryKindPosition-184] - _ = x[MemoryKindRange-185] - _ = x[MemoryKindElaboration-186] - _ = x[MemoryKindActivation-187] - _ = x[MemoryKindActivationEntries-188] - _ = x[MemoryKindVariableSizedSemaType-189] - _ = x[MemoryKindConstantSizedSemaType-190] - _ = x[MemoryKindDictionarySemaType-191] - _ = x[MemoryKindOptionalSemaType-192] - _ = x[MemoryKindIntersectionSemaType-193] - _ = x[MemoryKindReferenceSemaType-194] - _ = x[MemoryKindEntitlementSemaType-195] - _ = x[MemoryKindEntitlementMapSemaType-196] - _ = x[MemoryKindEntitlementRelationSemaType-197] - _ = x[MemoryKindCapabilitySemaType-198] - _ = x[MemoryKindInclusiveRangeSemaType-199] - _ = x[MemoryKindOrderedMap-200] - _ = x[MemoryKindOrderedMapEntryList-201] - _ = x[MemoryKindOrderedMapEntry-202] - _ = x[MemoryKindLast-203] + _ = x[MemoryKindCadenceSupertypeBound-98] + _ = x[MemoryKindCadenceEqualBound-99] + _ = x[MemoryKindCadenceNegationBound-100] + _ = x[MemoryKindCadenceConjunctionBound-101] + _ = x[MemoryKindRawString-102] + _ = x[MemoryKindAddressLocation-103] + _ = x[MemoryKindBytes-104] + _ = x[MemoryKindVariable-105] + _ = x[MemoryKindCompositeTypeInfo-106] + _ = x[MemoryKindCompositeField-107] + _ = x[MemoryKindInvocation-108] + _ = x[MemoryKindStorageMap-109] + _ = x[MemoryKindStorageKey-110] + _ = x[MemoryKindTypeToken-111] + _ = x[MemoryKindErrorToken-112] + _ = x[MemoryKindSpaceToken-113] + _ = x[MemoryKindProgram-114] + _ = x[MemoryKindIdentifier-115] + _ = x[MemoryKindArgument-116] + _ = x[MemoryKindBlock-117] + _ = x[MemoryKindFunctionBlock-118] + _ = x[MemoryKindParameter-119] + _ = x[MemoryKindParameterList-120] + _ = x[MemoryKindTypeParameter-121] + _ = x[MemoryKindTypeParameterList-122] + _ = x[MemoryKindTransfer-123] + _ = x[MemoryKindMembers-124] + _ = x[MemoryKindTypeAnnotation-125] + _ = x[MemoryKindDictionaryEntry-126] + _ = x[MemoryKindFunctionDeclaration-127] + _ = x[MemoryKindCompositeDeclaration-128] + _ = x[MemoryKindAttachmentDeclaration-129] + _ = x[MemoryKindInterfaceDeclaration-130] + _ = x[MemoryKindEntitlementDeclaration-131] + _ = x[MemoryKindEntitlementMappingElement-132] + _ = x[MemoryKindEntitlementMappingDeclaration-133] + _ = x[MemoryKindEnumCaseDeclaration-134] + _ = x[MemoryKindFieldDeclaration-135] + _ = x[MemoryKindTransactionDeclaration-136] + _ = x[MemoryKindImportDeclaration-137] + _ = x[MemoryKindVariableDeclaration-138] + _ = x[MemoryKindSpecialFunctionDeclaration-139] + _ = x[MemoryKindPragmaDeclaration-140] + _ = x[MemoryKindAssignmentStatement-141] + _ = x[MemoryKindBreakStatement-142] + _ = x[MemoryKindContinueStatement-143] + _ = x[MemoryKindEmitStatement-144] + _ = x[MemoryKindExpressionStatement-145] + _ = x[MemoryKindForStatement-146] + _ = x[MemoryKindIfStatement-147] + _ = x[MemoryKindReturnStatement-148] + _ = x[MemoryKindSwapStatement-149] + _ = x[MemoryKindSwitchStatement-150] + _ = x[MemoryKindWhileStatement-151] + _ = x[MemoryKindRemoveStatement-152] + _ = x[MemoryKindBooleanExpression-153] + _ = x[MemoryKindVoidExpression-154] + _ = x[MemoryKindNilExpression-155] + _ = x[MemoryKindStringExpression-156] + _ = x[MemoryKindIntegerExpression-157] + _ = x[MemoryKindFixedPointExpression-158] + _ = x[MemoryKindArrayExpression-159] + _ = x[MemoryKindDictionaryExpression-160] + _ = x[MemoryKindIdentifierExpression-161] + _ = x[MemoryKindInvocationExpression-162] + _ = x[MemoryKindMemberExpression-163] + _ = x[MemoryKindIndexExpression-164] + _ = x[MemoryKindConditionalExpression-165] + _ = x[MemoryKindUnaryExpression-166] + _ = x[MemoryKindBinaryExpression-167] + _ = x[MemoryKindFunctionExpression-168] + _ = x[MemoryKindCastingExpression-169] + _ = x[MemoryKindCreateExpression-170] + _ = x[MemoryKindDestroyExpression-171] + _ = x[MemoryKindReferenceExpression-172] + _ = x[MemoryKindForceExpression-173] + _ = x[MemoryKindPathExpression-174] + _ = x[MemoryKindAttachExpression-175] + _ = x[MemoryKindConstantSizedType-176] + _ = x[MemoryKindDictionaryType-177] + _ = x[MemoryKindFunctionType-178] + _ = x[MemoryKindInstantiationType-179] + _ = x[MemoryKindNominalType-180] + _ = x[MemoryKindOptionalType-181] + _ = x[MemoryKindReferenceType-182] + _ = x[MemoryKindIntersectionType-183] + _ = x[MemoryKindVariableSizedType-184] + _ = x[MemoryKindPosition-185] + _ = x[MemoryKindRange-186] + _ = x[MemoryKindElaboration-187] + _ = x[MemoryKindActivation-188] + _ = x[MemoryKindActivationEntries-189] + _ = x[MemoryKindVariableSizedSemaType-190] + _ = x[MemoryKindConstantSizedSemaType-191] + _ = x[MemoryKindDictionarySemaType-192] + _ = x[MemoryKindOptionalSemaType-193] + _ = x[MemoryKindIntersectionSemaType-194] + _ = x[MemoryKindReferenceSemaType-195] + _ = x[MemoryKindEntitlementSemaType-196] + _ = x[MemoryKindEntitlementMapSemaType-197] + _ = x[MemoryKindEntitlementRelationSemaType-198] + _ = x[MemoryKindCapabilitySemaType-199] + _ = x[MemoryKindInclusiveRangeSemaType-200] + _ = x[MemoryKindOrderedMap-201] + _ = x[MemoryKindOrderedMapEntryList-202] + _ = x[MemoryKindOrderedMapEntry-203] + _ = x[MemoryKindLast-204] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueCapabilityValueStorageReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeInclusiveRangeStaticTypeOptionalStaticTypeIntersectionStaticTypeEntitlementSetStaticAccessEntitlementMapStaticAccessReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceInclusiveRangeValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathValueCadenceTypeValueCadenceCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceInclusiveRangeTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceEntitlementSetAccessCadenceEntitlementMapAccessCadenceReferenceTypeCadenceIntersectionTypeCadenceCapabilityTypeCadenceEnumTypeCadenceSubtypeBoundCadenceEqualBoundCadenceNegationBoundCadenceConjunctionBoundRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEntitlementDeclarationEntitlementMappingElementEntitlementMappingDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeIntersectionTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeIntersectionSemaTypeReferenceSemaTypeEntitlementSemaTypeEntitlementMapSemaTypeEntitlementRelationSemaTypeCapabilitySemaTypeInclusiveRangeSemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueCapabilityValueStorageReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeInclusiveRangeStaticTypeOptionalStaticTypeIntersectionStaticTypeEntitlementSetStaticAccessEntitlementMapStaticAccessReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceInclusiveRangeValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathValueCadenceTypeValueCadenceCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceInclusiveRangeTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceEntitlementSetAccessCadenceEntitlementMapAccessCadenceReferenceTypeCadenceIntersectionTypeCadenceCapabilityTypeCadenceEnumTypeCadenceSubtypeBoundCadenceSupertypeBoundCadenceEqualBoundCadenceNegationBoundCadenceConjunctionBoundRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEntitlementDeclarationEntitlementMappingElementEntitlementMappingDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeIntersectionTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeIntersectionSemaTypeReferenceSemaTypeEntitlementSemaTypeEntitlementMapSemaTypeEntitlementRelationSemaTypeCapabilitySemaTypeInclusiveRangeSemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 176, 197, 220, 244, 261, 279, 285, 305, 319, 351, 383, 401, 423, 448, 464, 484, 507, 534, 550, 569, 588, 607, 630, 653, 673, 697, 715, 737, 763, 789, 808, 828, 846, 862, 882, 898, 916, 937, 956, 971, 989, 1010, 1033, 1055, 1081, 1100, 1122, 1144, 1168, 1194, 1218, 1244, 1265, 1286, 1310, 1334, 1354, 1374, 1390, 1406, 1428, 1448, 1467, 1496, 1525, 1546, 1571, 1583, 1599, 1619, 1636, 1655, 1676, 1692, 1711, 1737, 1765, 1793, 1812, 1839, 1866, 1886, 1909, 1930, 1945, 1964, 1981, 2001, 2024, 2033, 2048, 2053, 2061, 2078, 2092, 2102, 2112, 2122, 2131, 2141, 2151, 2158, 2168, 2176, 2181, 2194, 2203, 2216, 2229, 2246, 2254, 2261, 2275, 2290, 2309, 2329, 2350, 2370, 2392, 2417, 2446, 2465, 2481, 2503, 2520, 2539, 2565, 2582, 2601, 2615, 2632, 2645, 2664, 2676, 2687, 2702, 2715, 2730, 2744, 2759, 2776, 2790, 2803, 2819, 2836, 2856, 2871, 2891, 2911, 2931, 2947, 2962, 2983, 2998, 3014, 3032, 3049, 3065, 3082, 3101, 3116, 3130, 3146, 3163, 3177, 3189, 3206, 3217, 3229, 3242, 3258, 3275, 3283, 3288, 3299, 3309, 3326, 3347, 3368, 3386, 3402, 3422, 3439, 3458, 3480, 3507, 3525, 3547, 3557, 3576, 3591, 3595} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 176, 197, 220, 244, 261, 279, 285, 305, 319, 351, 383, 401, 423, 448, 464, 484, 507, 534, 550, 569, 588, 607, 630, 653, 673, 697, 715, 737, 763, 789, 808, 828, 846, 862, 882, 898, 916, 937, 956, 971, 989, 1010, 1033, 1055, 1081, 1100, 1122, 1144, 1168, 1194, 1218, 1244, 1265, 1286, 1310, 1334, 1354, 1374, 1390, 1406, 1428, 1448, 1467, 1496, 1525, 1546, 1571, 1583, 1599, 1619, 1636, 1655, 1676, 1692, 1711, 1737, 1765, 1793, 1812, 1839, 1866, 1886, 1909, 1930, 1945, 1964, 1985, 2002, 2022, 2045, 2054, 2069, 2074, 2082, 2099, 2113, 2123, 2133, 2143, 2152, 2162, 2172, 2179, 2189, 2197, 2202, 2215, 2224, 2237, 2250, 2267, 2275, 2282, 2296, 2311, 2330, 2350, 2371, 2391, 2413, 2438, 2467, 2486, 2502, 2524, 2541, 2560, 2586, 2603, 2622, 2636, 2653, 2666, 2685, 2697, 2708, 2723, 2736, 2751, 2765, 2780, 2797, 2811, 2824, 2840, 2857, 2877, 2892, 2912, 2932, 2952, 2968, 2983, 3004, 3019, 3035, 3053, 3070, 3086, 3103, 3122, 3137, 3151, 3167, 3184, 3198, 3210, 3227, 3238, 3250, 3263, 3279, 3296, 3304, 3309, 3320, 3330, 3347, 3368, 3389, 3407, 3423, 3443, 3460, 3479, 3501, 3528, 3546, 3568, 3578, 3597, 3612, 3616} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 85aaa34987..bbb1530a0a 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -237,6 +237,7 @@ var ( CadenceSubtypeBoundMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceSubtypeBound) CadenceEqualBoundMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceEqualBound) + CadenceSupertypeBoundMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceSupertypeBound) CadenceNegationBoundMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceNegationBound) CadenceConjunctionBoundMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceConjunctionBound) diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index 0d7baa786a..35c90d75db 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -506,6 +506,10 @@ func exportTypeBound( common.UseMemory(gauge, common.CadenceSubtypeBoundMemoryUsage) ty := ExportMeteredType(gauge, bound.Type, results) return cadence.NewSubtypeTypeBound(ty) + case sema.SupertypeTypeBound: + common.UseMemory(gauge, common.CadenceEqualBoundMemoryUsage) + ty := ExportMeteredType(gauge, bound.Type, results) + return cadence.NewSupertypeTypeBound(ty) case sema.EqualTypeBound: common.UseMemory(gauge, common.CadenceEqualBoundMemoryUsage) ty := ExportMeteredType(gauge, bound.Type, results) diff --git a/runtime/sema/typebound.go b/runtime/sema/typebound.go index 49f715c1ad..423a221bb8 100644 --- a/runtime/sema/typebound.go +++ b/runtime/sema/typebound.go @@ -137,6 +137,89 @@ func (b SubtypeTypeBound) withPrettyString(_ string) TypeBound { return b } +// SupertypeTypeBound(T) expresses the requirement that +// ∀U, U >= T + +type SupertypeTypeBound struct { + Type Type +} + +var _ TypeBound = SubtypeTypeBound{} + +func NewSupertypeTypeBound(ty Type) TypeBound { + return SupertypeTypeBound{Type: ty} +} + +func (SupertypeTypeBound) isTypeBound() {} + +func (b SupertypeTypeBound) Satisfies(ty Type) bool { + return IsSubType(b.Type, ty) +} + +func (b SupertypeTypeBound) HasInvalidType() bool { + return b.Type.IsInvalidType() +} + +func (b SupertypeTypeBound) Equal(bound TypeBound) bool { + other, ok := bound.(SupertypeTypeBound) + if !ok { + return false + } + return b.Type.Equal(other.Type) +} + +func (b SupertypeTypeBound) CheckInstantiated( + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), +) { + b.Type.CheckInstantiated(pos, memoryGauge, report) +} + +func (b SupertypeTypeBound) Map( + gauge common.MemoryGauge, + typeParamMap map[*TypeParameter]*TypeParameter, + f func(Type) Type, +) TypeBound { + return SupertypeTypeBound{ + Type: b.Type.Map(gauge, typeParamMap, f), + } +} + +func (b SupertypeTypeBound) TypeAnnotationState() TypeAnnotationState { + return b.Type.TypeAnnotationState() +} + +func (b SupertypeTypeBound) RewriteWithIntersectionTypes() (result TypeBound, rewritten bool) { + rewrittenType, rewritten := b.Type.RewriteWithIntersectionTypes() + if rewritten { + return SupertypeTypeBound{ + Type: rewrittenType, + }, true + } + return b, false +} + +func (b SupertypeTypeBound) And(bound TypeBound) TypeBound { + return NewConjunctionTypeBound([]TypeBound{b, bound}) +} + +func (b SupertypeTypeBound) Or(bound TypeBound) TypeBound { + return NewDisjunctionTypeBound([]TypeBound{b, bound}) +} + +func (b SupertypeTypeBound) Not() TypeBound { + return NewNegationTypeBound(b) +} + +func (b SupertypeTypeBound) String() string { + return fmt.Sprintf(">=: %s", b.Type.QualifiedString()) +} + +func (b SupertypeTypeBound) withPrettyString(_ string) TypeBound { + return b +} + // EqualTypeBound expresses the requirement that // ∀U, U = T @@ -450,9 +533,9 @@ func (b ConjunctionTypeBound) withPrettyString(prettyString string) TypeBound { } // Any other kinds of type bounds we might wish to express can be -// written as the composition of `<=`, `=`, `!` and `&`. Technically, `=` is not -// really even necessary, as `U = T` is equivalent to `U <= T & T <= U`, but for -// performance reasons we give it its own basic bound +// written as the composition of `<=`, `>=`, `=`, `!` and `&&`. Technically, `=` is not +// really even necessary, as `U = T` is equivalent to `U <= T && U >= T`, but for +// performance reasons we give it its own basic bound. // `U <= T && !(T = U) ==> U < T` func NewStrictSubtypeTypeBound(ty Type) TypeBound { @@ -462,21 +545,15 @@ func NewStrictSubtypeTypeBound(ty Type) TypeBound { withPrettyString(fmt.Sprintf("<: %s", ty.String())) } -// `!(U <= T) ==> U > T` +// `U >= T && !(T = U) ==> U > T` func NewStrictSupertypeTypeBound(ty Type) TypeBound { - return NewSubtypeTypeBound(ty). + return NewEqualTypeBound(ty). Not(). + And(NewSupertypeTypeBound(ty)). withPrettyString(fmt.Sprintf(">: %s", ty.String())) } -// `!(U < T) ==> U >= T` -func NewSupertypeTypeBound(ty Type) TypeBound { - return NewStrictSubtypeTypeBound(ty). - Not(). - withPrettyString(fmt.Sprintf(">=: %s", ty.String())) -} - -// `!(!B1 & ... & !Bn) ==> B1 || ... || Bn` +// `!(!B1 && ... && !Bn) ==> B1 || ... || Bn` func NewDisjunctionTypeBound(typeBounds []TypeBound) TypeBound { var negatedTypeBounds []TypeBound var strs []string diff --git a/runtime/sema/typebound_test.go b/runtime/sema/typebound_test.go index 1624156b78..0463fee1dc 100644 --- a/runtime/sema/typebound_test.go +++ b/runtime/sema/typebound_test.go @@ -136,7 +136,8 @@ func TestTypeBound_Satisfies(t *testing.T) { NewSupertypeTypeBound(IntType), }) - assert.True(t, typeBound.Satisfies(FixedSizeUnsignedIntegerType)) + assert.False(t, typeBound.Satisfies(FixedSizeUnsignedIntegerType)) + assert.True(t, typeBound.Satisfies(IntegerType)) assert.True(t, typeBound.Satisfies(AnyStructType)) assert.True(t, typeBound.Satisfies(IntType)) assert.False(t, typeBound.Satisfies(StringType)) diff --git a/types.go b/types.go index d71677c6a2..9f8c77bba4 100644 --- a/types.go +++ b/types.go @@ -1846,6 +1846,31 @@ func (b SubtypeTypeBound) Equal(other TypeBound) bool { return b.Type.Equal(otherBound.Type) } +type SupertypeTypeBound struct { + Type Type +} + +var _ TypeBound = SupertypeTypeBound{} + +func NewSupertypeTypeBound(ty Type) SupertypeTypeBound { + return SupertypeTypeBound{Type: ty} +} + +func (SupertypeTypeBound) isTypeBound() {} + +func (b SupertypeTypeBound) ID() string { + return fmt.Sprintf(">=: %s", b.Type.ID()) +} + +func (b SupertypeTypeBound) Equal(other TypeBound) bool { + otherBound, ok := other.(SupertypeTypeBound) + if !ok { + return false + } + + return b.Type.Equal(otherBound.Type) +} + type EqualTypeBound struct { Type Type } From 7e8d0d50ad105caf07d286ac56c528a95f71a6f5 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 3 Apr 2024 16:37:55 -0400 Subject: [PATCH 15/15] generate --- runtime/sema/account.gen.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/runtime/sema/account.gen.go b/runtime/sema/account.gen.go index 1431a9cae3..dc2f046987 100644 --- a/runtime/sema/account.gen.go +++ b/runtime/sema/account.gen.go @@ -127,8 +127,10 @@ All storage paths of this account. const Account_StorageTypeSaveFunctionName = "save" var Account_StorageTypeSaveFunctionTypeParameterT = &TypeParameter{ - Name: "T", - TypeBound: SubtypeTypeBound{Type: StorableType}, + Name: "T", + TypeBound: SubtypeTypeBound{ + Type: StorableType, + }, } var Account_StorageTypeSaveFunctionType = &FunctionType{ @@ -193,8 +195,10 @@ The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is al const Account_StorageTypeLoadFunctionName = "load" var Account_StorageTypeLoadFunctionTypeParameterT = &TypeParameter{ - Name: "T", - TypeBound: SubtypeTypeBound{Type: StorableType}, + Name: "T", + TypeBound: SubtypeTypeBound{ + Type: StorableType, + }, } var Account_StorageTypeLoadFunctionType = &FunctionType{ @@ -236,8 +240,10 @@ The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is al const Account_StorageTypeCopyFunctionName = "copy" var Account_StorageTypeCopyFunctionTypeParameterT = &TypeParameter{ - Name: "T", - TypeBound: SubtypeTypeBound{Type: AnyStructType}, + Name: "T", + TypeBound: SubtypeTypeBound{ + Type: AnyStructType, + }, } var Account_StorageTypeCopyFunctionType = &FunctionType{ @@ -279,8 +285,10 @@ The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is al const Account_StorageTypeCheckFunctionName = "check" var Account_StorageTypeCheckFunctionTypeParameterT = &TypeParameter{ - Name: "T", - TypeBound: SubtypeTypeBound{Type: AnyType}, + Name: "T", + TypeBound: SubtypeTypeBound{ + Type: AnyType, + }, } var Account_StorageTypeCheckFunctionType = &FunctionType{