diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 45fe760098..59dcc4dedb 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2048,7 +2048,7 @@ func (checker *Checker) checkTypeAnnotation(typeAnnotation TypeAnnotation, pos a } checker.checkInvalidInterfaceAsType(typeAnnotation.Type, pos) - checker.checkParameterizedTypeIsInstantiated(typeAnnotation.Type, pos) + typeAnnotation.Type.CheckInstantiated(pos, checker.memoryGauge, checker.report) } func (checker *Checker) checkInvalidInterfaceAsType(ty Type, pos ast.HasPosition) { @@ -2068,49 +2068,6 @@ func (checker *Checker) checkInvalidInterfaceAsType(ty Type, pos ast.HasPosition } } -func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.HasPosition) { - parameterizedType, ok := ty.(ParameterizedType) - if !ok { - return - } - - typeArgs := parameterizedType.TypeArguments() - typeParameters := parameterizedType.TypeParameters() - - typeArgumentCount := len(typeArgs) - typeParameterCount := len(typeParameters) - - if typeArgumentCount != typeParameterCount { - checker.report( - &InvalidTypeArgumentCountError{ - TypeParameterCount: typeParameterCount, - TypeArgumentCount: typeArgumentCount, - Range: ast.NewRange( - checker.memoryGauge, - pos.StartPosition(), - pos.EndPosition(checker.memoryGauge), - ), - }, - ) - } - - // Ensure that each non-optional typeparameter is non-nil. - for index, typeParam := range typeParameters { - if !typeParam.Optional && typeArgs[index] == nil { - checker.report( - &MissingTypeArgumentError{ - TypeArgumentName: typeParam.Name, - Range: ast.NewRange( - checker.memoryGauge, - pos.StartPosition(), - pos.EndPosition(checker.memoryGauge), - ), - }, - ) - } - } -} - func (checker *Checker) ValueActivationDepth() int { return checker.valueActivations.Depth() } diff --git a/runtime/sema/simple_type.go b/runtime/sema/simple_type.go index 1e8b30b0df..c9ea9941bf 100644 --- a/runtime/sema/simple_type.go +++ b/runtime/sema/simple_type.go @@ -168,3 +168,7 @@ func (t *SimpleType) CompositeKind() common.CompositeKind { return common.CompositeKindStructure } } + +func (t *SimpleType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP +} diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 20db7a64cc..6343f6e6aa 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -174,6 +174,8 @@ type Type interface { Resolve(typeArguments *TypeParameterTypeOrderedMap) Type GetMembers() map[string]MemberResolver + + CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) } // ValueIndexableType is a type which can be indexed into using a value @@ -293,6 +295,34 @@ func MustInstantiate(t ParameterizedType, typeArguments ...Type) Type { ) } +func CheckParameterizedTypeInstantiated( + t ParameterizedType, + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), +) { + typeArgs := t.TypeArguments() + typeParameters := t.TypeParameters() + + // The check for the argument and parameter count already happens in the checker, so we skip that here. + + // Ensure that each non-optional typeparameter is non-nil. + for index, typeParam := range typeParameters { + if !typeParam.Optional && typeArgs[index] == nil { + report( + &MissingTypeArgumentError{ + TypeArgumentName: typeParam.Name, + Range: ast.NewRange( + memoryGauge, + pos.StartPosition(), + pos.EndPosition(memoryGauge), + ), + }, + ) + } + } +} + // TypeAnnotation type TypeAnnotation struct { @@ -694,6 +724,10 @@ func (t *OptionalType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type } } +func (t *OptionalType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.Type.CheckInstantiated(pos, memoryGauge, report) +} + const optionalTypeMapFunctionDocString = ` Returns an optional of the result of calling the given function with the value of this optional when it is not nil. @@ -903,6 +937,10 @@ func (t *GenericType) GetMembers() map[string]MemberResolver { return withBuiltinMembers(t, nil) } +func (t *GenericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.TypeParameter.TypeBound.CheckInstantiated(pos, memoryGauge, report) +} + // IntegerRangedType type IntegerRangedType interface { @@ -1176,6 +1214,10 @@ func (t *NumericType) IsSuperType() bool { return t.isSuperType } +func (*NumericType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP +} + // FixedPointNumericType represents all the types in the fixed-point range. type FixedPointNumericType struct { maxFractional *big.Int @@ -1369,6 +1411,10 @@ func (t *FixedPointNumericType) IsSuperType() bool { return t.isSuperType } +func (*FixedPointNumericType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP +} + // Numeric types var ( @@ -2551,6 +2597,10 @@ func (t *VariableSizedType) Resolve(typeArguments *TypeParameterTypeOrderedMap) } } +func (t *VariableSizedType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.ElementType(false).CheckInstantiated(pos, memoryGauge, report) +} + // ConstantSizedType is a constant sized array type type ConstantSizedType struct { Type Type @@ -2707,6 +2757,10 @@ func (t *ConstantSizedType) Resolve(typeArguments *TypeParameterTypeOrderedMap) } } +func (t *ConstantSizedType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.ElementType(false).CheckInstantiated(pos, memoryGauge, report) +} + // Parameter func formatParameter(spaces bool, label, identifier, typeAnnotation string) string { @@ -3372,6 +3426,18 @@ func (t *FunctionType) initializeMemberResolvers() { }) } +func (t *FunctionType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + for _, tyParam := range t.TypeParameters { + tyParam.TypeBound.CheckInstantiated(pos, memoryGauge, report) + } + + for _, param := range t.Parameters { + param.TypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) + } + + t.ReturnTypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) +} + type ArgumentExpressionsCheck func( checker *Checker, argumentExpressions []ast.Expression, @@ -4362,6 +4428,24 @@ func (t *CompositeType) SetNestedType(name string, nestedType ContainedType) { nestedType.SetContainerType(t) } +func (t *CompositeType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + if t.EnumRawType != nil { + t.EnumRawType.CheckInstantiated(pos, memoryGauge, report) + } + + if t.baseType != nil { + t.baseType.CheckInstantiated(pos, memoryGauge, report) + } + + for _, typ := range t.ImplicitTypeRequirementConformances { + typ.CheckInstantiated(pos, memoryGauge, report) + } + + for _, typ := range t.ExplicitInterfaceConformances { + typ.CheckInstantiated(pos, memoryGauge, report) + } +} + // Member type Member struct { @@ -4848,6 +4932,12 @@ func (t *InterfaceType) FieldPosition(name string, declaration *ast.InterfaceDec return declaration.Members.FieldPosition(name, declaration.CompositeKind) } +func (t *InterfaceType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + for _, param := range t.InitializerParameters { + param.TypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) + } +} + // DictionaryType consists of the key and value type // for all key-value pairs in the dictionary: // All keys have to be a subtype of the key type, @@ -4977,6 +5067,11 @@ func (t *DictionaryType) RewriteWithRestrictedTypes() (Type, bool) { } } +func (t *DictionaryType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.KeyType.CheckInstantiated(pos, memoryGauge, report) + t.ValueType.CheckInstantiated(pos, memoryGauge, report) +} + const dictionaryTypeContainsKeyFunctionDocString = ` Returns true if the given key is in the dictionary ` @@ -5448,6 +5543,10 @@ func (t *InclusiveRangeType) TypeArguments() []Type { } } +func (t *InclusiveRangeType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + CheckParameterizedTypeInstantiated(t, pos, memoryGauge, report) +} + var inclusiveRangeTypeParameter = &TypeParameter{ Name: "T", TypeBound: IntegerType, @@ -5806,6 +5905,10 @@ func (t *ReferenceType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type } } +func (t *ReferenceType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.Type.CheckInstantiated(pos, memoryGauge, report) +} + const AddressTypeName = "Address" // AddressType represents the address type @@ -5901,6 +6004,10 @@ func (t *AddressType) Resolve(_ *TypeParameterTypeOrderedMap) Type { return t } +func (*AddressType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP +} + const AddressTypeToBytesFunctionName = `toBytes` var AddressTypeToBytesFunctionType = &FunctionType{ @@ -6816,6 +6923,16 @@ func (t *TransactionType) Resolve(_ *TypeParameterTypeOrderedMap) Type { return t } +func (t *TransactionType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + for _, param := range t.PrepareParameters { + param.TypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) + } + + for _, param := range t.Parameters { + param.TypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) + } +} + // RestrictedType // // No restrictions implies the type is fully restricted, @@ -7121,6 +7238,10 @@ func (t *RestrictedType) IsValidIndexingType(ty Type) bool { attachmentType.IsResourceType() == t.IsResourceType() } +func (t *RestrictedType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.Type.CheckInstantiated(pos, memoryGauge, report) +} + // CapabilityType type CapabilityType struct { @@ -7324,6 +7445,10 @@ func (t *CapabilityType) TypeArguments() []Type { } } +func (t *CapabilityType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + CheckParameterizedTypeInstantiated(t, pos, memoryGauge, report) +} + func CapabilityTypeBorrowFunctionType(borrowType Type) *FunctionType { var typeParameters []*TypeParameter diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 9907de2742..ae6ae2b379 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -404,13 +404,15 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { t.Parallel() - baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) - baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) - - options := ParseAndCheckOptions{ - Config: &sema.Config{ - BaseValueActivation: baseValueActivation, - }, + newOptions := func() ParseAndCheckOptions { + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + return ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + } } test := func(t *testing.T, ty sema.Type) { @@ -421,7 +423,7 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { let a: %[1]s = 0 let b: %[1]s = 10 var range = InclusiveRange<%[1]s>(a, b) - `, ty), options) + `, ty), newOptions()) errs := RequireCheckerErrors(t, err, 1) assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) @@ -434,7 +436,7 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { let a: %[1]s = 0 let b: %[1]s = 10 var range: InclusiveRange<%[1]s> = InclusiveRange<%[1]s>(a, b) - `, ty), options) + `, ty), newOptions()) // One for the invocation and another for the type. errs := RequireCheckerErrors(t, err, 2) @@ -448,7 +450,7 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { _, err := ParseAndCheckWithOptions(t, fmt.Sprintf(` let a: InclusiveRange = InclusiveRange(0, 10) let b: InclusiveRange<%s> = a - `, ty), options) + `, ty), newOptions()) errs := RequireCheckerErrors(t, err, 1) assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 7167facadf..6fd00c22fb 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" ) @@ -54,29 +55,6 @@ func TestCheckTypeArguments(t *testing.T) { require.Nil(t, capType.(*sema.CapabilityType).BorrowType) }) - t.Run("inclusive range, no instantiation", func(t *testing.T) { - - t.Parallel() - - baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) - baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) - - _, err := ParseAndCheckWithOptions(t, - ` - let inclusiveRange: InclusiveRange = InclusiveRange(1, 10) - `, - ParseAndCheckOptions{ - Config: &sema.Config{ - BaseValueActivation: baseValueActivation, - }, - }, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) - }) - t.Run("inclusive range, instantiation with more than arguments", func(t *testing.T) { t.Parallel() @@ -178,6 +156,800 @@ func TestCheckTypeArguments(t *testing.T) { }) } +func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { + + t.Parallel() + + test := func(t *testing.T, code string) error { + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + options := ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + } + + _, err := ParseAndCheckWithOptions(t, code, options) + return err + } + + t.Run("InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + ) + + require.NoError(t, err) + }) + + t.Run("InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + ) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.InvalidTypeArgumentCountError{}, errs[0]) + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[1]) + }) + + t.Run("InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("VariableSizedArray with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let r: [InclusiveRange] = []", + ) + + require.NoError(t, err) + }) + + t.Run("VariableSizedArray with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let r: [InclusiveRange] = []", + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("ConstantSizedType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", + ) + + require.NoError(t, err) + }) + + t.Run("ConstantSizedType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("OptionalType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let r: InclusiveRange? = nil", + ) + + require.NoError(t, err) + }) + + t.Run("OptionalType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let r: InclusiveRange? = nil", + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("DictionaryType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let r: {Int: InclusiveRange} = {}", + ) + + require.NoError(t, err) + }) + + t.Run("DictionaryType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let r: {Int: InclusiveRange} = {}", + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Nested Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + struct Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + struct Foo { + let bar: Bar + + init(b : Bar) { + self.bar = b + } + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("Nested Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + struct Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + struct Foo { + let bar: Bar + + init(b : Bar) { + self.bar = b + } + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Contract with Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + contract C { + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + contract C { + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Struct with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + struct Bar { + let f: ((): InclusiveRange) + + init() { + self.f = fun () : InclusiveRange { + return InclusiveRange(1, 10) + } + } + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("Struct with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + struct Bar { + let f: ((): InclusiveRange) + + init() { + self.f = fun () : InclusiveRange { + return InclusiveRange(1, 10) + } + } + } + `, + ) + + errs := RequireCheckerErrors(t, err, 2) + + // 2 errors for the two occurrences of InclusiveRange. + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[1]) + }) + + t.Run("StructInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + struct interface Bar { + fun getRange(): InclusiveRange + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("StructInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + struct interface Bar { + fun getRange(): InclusiveRange + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + resource Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with StructInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + contract C { + struct interface Foo { + fun getRange(): InclusiveRange + } + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with StructInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + contract C { + struct interface Foo { + fun getRange(): InclusiveRange + } + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + resource Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Nested Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + resource Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + resource Foo { + let bar: @Bar + + init(b : @Bar) { + self.bar <- b + } + + destroy() { + destroy self.bar + } + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("Nested Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + resource Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + resource Foo { + let bar: @Bar + + init(b : @Bar) { + self.bar <- b + } + + destroy() { + destroy self.bar + } + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("ResourceInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + resource interface Bar { + fun getRange(): InclusiveRange + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("ResourceInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + resource interface Bar { + fun getRange(): InclusiveRange + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Contract with ResourceInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + contract C { + resource interface Foo { + fun getRange(): InclusiveRange + } + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with ResourceInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + contract C { + resource interface Foo { + fun getRange(): InclusiveRange + } + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Type with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + pub fun main(): Type { + return Type>() + } + `, + ) + + require.NoError(t, err) + }) + + t.Run("Type with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + pub fun main(): Type { + return Type() + } + `, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Event with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "event Foo(bar: InclusiveRange)", + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.InvalidEventParameterTypeError{}, errs[0]) + }) + + t.Run("Event with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "event Foo(bar: InclusiveRange)", + ) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + assert.IsType(t, &sema.InvalidEventParameterTypeError{}, errs[1]) + }) + + t.Run("Enum Declaration", func(t *testing.T) { + + t.Parallel() + + err := test(t, + ` + pub fun main(): Direction { + return Direction.RIGHT + } + + pub enum Direction: Int { + pub case UP + pub case DOWN + pub case LEFT + pub case RIGHT + } + `, + ) + + require.NoError(t, err) + }) + + testFunctionTpe := func(t *testing.T, functionType *sema.FunctionType) error { + baseTypeActivation := sema.NewVariableActivation(sema.BaseTypeActivation) + baseTypeActivation.DeclareType(stdlib.StandardLibraryType{ + Name: "TestFunc", + Kind: common.DeclarationKindType, + Type: functionType, + }) + + options := ParseAndCheckOptions{ + Config: &sema.Config{ + BaseTypeActivation: baseTypeActivation, + }, + } + + _, err := ParseAndCheckWithOptions(t, "fun test(testFunc: TestFunc) {}", options) + return err + } + + t.Run("Function type, return type not instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.InclusiveRangeType{}, + ), + }, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Function type, return type instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, + ), + }, + ) + + require.NoError(t, err) + }) + + t.Run("Function type, parameter type not instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Identifier: "a", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.InclusiveRangeType{}, + ), + }, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + }, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Function type, parameter type instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Identifier: "a", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, + ), + }, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + }, + ) + + require.NoError(t, err) + }) + + t.Run("Function type, type parameter type bound not instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + { + Name: "T", + TypeBound: &sema.InclusiveRangeType{}, + }, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + }, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Function type,type parameter type bound instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + { + Name: "T", + TypeBound: &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, + }, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + }, + ) + + require.NoError(t, err) + }) + +} + func TestCheckTypeArgumentSubtyping(t *testing.T) { t.Parallel() @@ -261,36 +1033,15 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { t.Parallel() - checker, err := parseAndCheckWithTestValue(t, + _, err := parseAndCheckWithTestValue(t, ` let cap: Capability = test let cap2: Capability<&Int> = cap `, &sema.CapabilityType{}, ) - require.NotNil(t, checker) - - capType := RequireGlobalValue(t, checker.Elaboration, "cap") - require.IsType(t, - &sema.CapabilityType{}, - capType, - ) - require.Nil(t, capType.(*sema.CapabilityType).BorrowType) - - cap2Type := RequireGlobalValue(t, checker.Elaboration, "cap2") - require.IsType(t, - &sema.CapabilityType{}, - cap2Type, - ) - require.Equal(t, - &sema.ReferenceType{ - Type: sema.IntType, - }, - cap2Type.(*sema.CapabilityType).BorrowType, - ) errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) @@ -298,7 +1049,7 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { t.Parallel() - checker, err := parseAndCheckWithTestValue(t, + _, err := parseAndCheckWithTestValue(t, ` let cap: Capability<&String> = test let cap2: Capability<&Int> = cap @@ -309,28 +1060,8 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { }, }, ) - require.NotNil(t, checker) - - assert.Equal(t, - &sema.CapabilityType{ - BorrowType: &sema.ReferenceType{ - Type: sema.StringType, - }, - }, - RequireGlobalValue(t, checker.Elaboration, "cap"), - ) - - assert.Equal(t, - &sema.CapabilityType{ - BorrowType: &sema.ReferenceType{ - Type: sema.IntType, - }, - }, - RequireGlobalValue(t, checker.Elaboration, "cap2"), - ) errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) }