From da3dfc238c2dbef3b30dea6fb1bedc86ae1d5bdb Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 31 Oct 2023 21:31:37 +0530 Subject: [PATCH 01/13] Make checkParameterizedTypeIsInstantiated recursive --- runtime/sema/checker.go | 105 ++++++++++++++++----- runtime/tests/checker/typeargument_test.go | 96 ++++++++++++++----- 2 files changed, 152 insertions(+), 49 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 45fe760098..830b9be728 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2069,37 +2069,74 @@ 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 - } + switch t := ty.(type) { + case *OptionalType: + checker.checkParameterizedTypeIsInstantiated(t.Type, pos) - typeArgs := parameterizedType.TypeArguments() - typeParameters := parameterizedType.TypeParameters() + case ArrayType: + checker.checkParameterizedTypeIsInstantiated(t.ElementType(false), pos) - typeArgumentCount := len(typeArgs) - typeParameterCount := len(typeParameters) + case *DictionaryType: + checker.checkParameterizedTypeIsInstantiated(t.KeyType, pos) + checker.checkParameterizedTypeIsInstantiated(t.ValueType, pos) - if typeArgumentCount != typeParameterCount { - checker.report( - &InvalidTypeArgumentCountError{ - TypeParameterCount: typeParameterCount, - TypeArgumentCount: typeArgumentCount, - Range: ast.NewRange( - checker.memoryGauge, - pos.StartPosition(), - pos.EndPosition(checker.memoryGauge), - ), - }, - ) - } + case *ReferenceType: + checker.checkParameterizedTypeIsInstantiated(t.Type, pos) + + case *FunctionType: + for _, tyParam := range t.TypeParameters { + checker.checkParameterizedTypeIsInstantiated(tyParam.TypeBound, pos) + } + + for _, param := range t.Parameters { + checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) + } + + checker.checkParameterizedTypeIsInstantiated(t.ReturnTypeAnnotation.Type, pos) - // Ensure that each non-optional typeparameter is non-nil. - for index, typeParam := range typeParameters { - if !typeParam.Optional && typeArgs[index] == nil { + case *RestrictedType: + checker.checkParameterizedTypeIsInstantiated(t.Type, pos) + + case *CompositeType: + if t.EnumRawType != nil { + checker.checkParameterizedTypeIsInstantiated(t.EnumRawType, pos) + } + if t.containerType != nil { + checker.checkParameterizedTypeIsInstantiated(t.containerType, pos) + } + if t.baseType != nil { + checker.checkParameterizedTypeIsInstantiated(t.baseType, pos) + } + + for _, typ := range t.ImplicitTypeRequirementConformances { + checker.checkParameterizedTypeIsInstantiated(typ, pos) + } + + for _, typ := range t.ExplicitInterfaceConformances { + checker.checkParameterizedTypeIsInstantiated(typ, pos) + } + + case *InterfaceType: + if t.containerType != nil { + checker.checkParameterizedTypeIsInstantiated(t.containerType, pos) + } + + for _, param := range t.InitializerParameters { + checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) + } + + case ParameterizedType: + typeArgs := t.TypeArguments() + typeParameters := t.TypeParameters() + + typeArgumentCount := len(typeArgs) + typeParameterCount := len(typeParameters) + + if typeArgumentCount != typeParameterCount { checker.report( - &MissingTypeArgumentError{ - TypeArgumentName: typeParam.Name, + &InvalidTypeArgumentCountError{ + TypeParameterCount: typeParameterCount, + TypeArgumentCount: typeArgumentCount, Range: ast.NewRange( checker.memoryGauge, pos.StartPosition(), @@ -2108,6 +2145,22 @@ func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.Ha }, ) } + + // 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), + ), + }, + ) + } + } } } diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 7167facadf..7cc81f2669 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -54,29 +54,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 +155,79 @@ func TestCheckTypeArguments(t *testing.T) { }) } +type checkParameterizedTypeIsInstantiatedTestCase struct { + name string + code string +} + +func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { + + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + options := ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + } + + runTestCase := func(t *testing.T, testCase checkParameterizedTypeIsInstantiatedTestCase) { + t.Run(testCase.name, func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + testCase.code, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + } + + testCases := []checkParameterizedTypeIsInstantiatedTestCase{ + { + name: "InclusiveRange", + code: "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + }, + { + name: "VariableSizedArray with InclusiveRange", + code: "let r: [InclusiveRange] = []", + }, + { + name: "ConstantSizedType with InclusiveRange", + code: "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", + }, + { + name: "OptionalType with InclusiveRange", + code: "let r: InclusiveRange? = nil", + }, + { + name: "DictionaryType with InclusiveRange", + code: "let r: {Int: InclusiveRange} = {}", + }, + { + name: "Struct with InclusiveRange", + code: ` + pub struct Foo { + pub let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + }, + } + + for _, test := range testCases { + runTestCase(t, test) + } +} + func TestCheckTypeArgumentSubtyping(t *testing.T) { t.Parallel() From 951a4a7b79ee806bc5167c1f1da40fc6d8a69b9d Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 3 Nov 2023 21:21:41 +0530 Subject: [PATCH 02/13] Remove access modifiers from tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/typeargument_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 7cc81f2669..8699cb54c1 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -212,8 +212,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { { name: "Struct with InclusiveRange", code: ` - pub struct Foo { - pub let a: InclusiveRange + struct Foo { + let a: InclusiveRange init() { self.a = InclusiveRange(1, 10) From a1fcc7d529e39be525d956fdad6b3ba34407eb6a Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 3 Nov 2023 21:32:05 +0530 Subject: [PATCH 03/13] Move checkParameterizedTypeIsInstantiatedTestCase inside the test case --- runtime/tests/checker/typeargument_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 8699cb54c1..78eddb3002 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -155,11 +155,6 @@ func TestCheckTypeArguments(t *testing.T) { }) } -type checkParameterizedTypeIsInstantiatedTestCase struct { - name string - code string -} - func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() @@ -172,6 +167,11 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { }, } + type checkParameterizedTypeIsInstantiatedTestCase struct { + name string + code string + } + runTestCase := func(t *testing.T, testCase checkParameterizedTypeIsInstantiatedTestCase) { t.Run(testCase.name, func(t *testing.T) { From 6e05d78b3a63d88b1aee5f2af43145e96c9f2150 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 4 Nov 2023 15:28:34 +0530 Subject: [PATCH 04/13] Add more test cases for structs, resources, contracts, enums --- runtime/sema/checker.go | 7 - runtime/tests/checker/typeargument_test.go | 614 +++++++++++++++++++-- 2 files changed, 570 insertions(+), 51 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 830b9be728..8d3d2e1ac9 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2101,9 +2101,6 @@ func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.Ha if t.EnumRawType != nil { checker.checkParameterizedTypeIsInstantiated(t.EnumRawType, pos) } - if t.containerType != nil { - checker.checkParameterizedTypeIsInstantiated(t.containerType, pos) - } if t.baseType != nil { checker.checkParameterizedTypeIsInstantiated(t.baseType, pos) } @@ -2117,10 +2114,6 @@ func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.Ha } case *InterfaceType: - if t.containerType != nil { - checker.checkParameterizedTypeIsInstantiated(t.containerType, pos) - } - for _, param := range t.InitializerParameters { checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) } diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 78eddb3002..25c3044d81 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -167,51 +167,162 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { }, } - type checkParameterizedTypeIsInstantiatedTestCase struct { - name string - code string - } + t.Run("InclusiveRange", func(t *testing.T) { - runTestCase := func(t *testing.T, testCase checkParameterizedTypeIsInstantiatedTestCase) { - t.Run(testCase.name, func(t *testing.T) { + t.Parallel() - t.Parallel() + _, err := ParseAndCheckWithOptions(t, + "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + options, + ) - _, err := ParseAndCheckWithOptions(t, - testCase.code, - options, - ) + require.NoError(t, err) + }) - errs := RequireCheckerErrors(t, err, 1) + t.Run("InclusiveRange", func(t *testing.T) { - assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) - }) - } + t.Parallel() - testCases := []checkParameterizedTypeIsInstantiatedTestCase{ - { - name: "InclusiveRange", - code: "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", - }, - { - name: "VariableSizedArray with InclusiveRange", - code: "let r: [InclusiveRange] = []", - }, - { - name: "ConstantSizedType with InclusiveRange", - code: "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", - }, - { - name: "OptionalType with InclusiveRange", - code: "let r: InclusiveRange? = nil", - }, - { - name: "DictionaryType with InclusiveRange", - code: "let r: {Int: InclusiveRange} = {}", - }, - { - name: "Struct with InclusiveRange", - code: ` + _, err := ParseAndCheckWithOptions(t, + "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("VariableSizedArray with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: [InclusiveRange] = []", + options, + ) + + require.NoError(t, err) + }) + + t.Run("VariableSizedArray with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: [InclusiveRange] = []", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("ConstantSizedType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", + options, + ) + + require.NoError(t, err) + }) + + t.Run("ConstantSizedType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("OptionalType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: InclusiveRange? = nil", + options, + ) + + require.NoError(t, err) + }) + + t.Run("OptionalType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: InclusiveRange? = nil", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("DictionaryType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: {Int: InclusiveRange} = {}", + options, + ) + + require.NoError(t, err) + }) + + t.Run("DictionaryType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: {Int: InclusiveRange} = {}", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` struct Foo { let a: InclusiveRange @@ -220,12 +331,427 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - }, - } + options, + ) - for _, test := range testCases { - runTestCase(t, test) - } + 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 := ParseAndCheckWithOptions(t, + ` + struct Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + struct Foo { + let bar: Bar + + init(b : Bar) { + self.bar = b + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Nested Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + struct Foo { + let bar: Bar + + init(b : Bar) { + self.bar = b + } + } + `, + options, + ) + + 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 := ParseAndCheckWithOptions(t, + ` + contract C { + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + } + `, + options, + ) + + 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 := ParseAndCheckWithOptions(t, + ` + struct Bar { + let f: ((): InclusiveRange) + + init() { + self.f = fun () : InclusiveRange { + return InclusiveRange(1, 10) + } + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Struct with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct Bar { + let f: ((): InclusiveRange) + + init() { + self.f = fun () : InclusiveRange { + return InclusiveRange(1, 10) + } + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 2) + + // 2 errors for the two occurences 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 := ParseAndCheckWithOptions(t, + ` + struct interface Bar { + fun getRange(): InclusiveRange + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("StructInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct interface Bar { + fun getRange(): InclusiveRange + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + resource Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + resource Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + options, + ) + + 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 := ParseAndCheckWithOptions(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 + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Nested Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(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 + } + } + `, + options, + ) + + 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 := ParseAndCheckWithOptions(t, + ` + resource interface Bar { + fun getRange(): InclusiveRange + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("ResourceInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + resource interface Bar { + fun getRange(): InclusiveRange + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Type with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + pub fun main(): Type { + return Type>() + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Type with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + pub fun main(): Type { + return Type() + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Event with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "event Foo(bar: InclusiveRange)", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.InvalidEventParameterTypeError{}, errs[0]) + }) + + t.Run("Event with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "event Foo(bar: InclusiveRange)", + options, + ) + + 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 := ParseAndCheckWithOptions(t, + ` + pub fun main(): Direction { + return Direction.RIGHT + } + + pub enum Direction: Int { + pub case UP + pub case DOWN + pub case LEFT + pub case RIGHT + } + `, + options, + ) + + require.NoError(t, err) + }) } func TestCheckTypeArgumentSubtyping(t *testing.T) { From 151816e5a29ab836eb0d331e720807e8a740afd2 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 4 Nov 2023 15:34:44 +0530 Subject: [PATCH 05/13] Add test cases for struct/resource interface inside a contract --- runtime/tests/checker/typeargument_test.go | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 25c3044d81..5c6d242edf 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -545,6 +545,44 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { require.NoError(t, err) }) + t.Run("Contract with StructInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + struct interface Foo { + fun getRange(): InclusiveRange + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with StructInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + struct interface Foo { + fun getRange(): InclusiveRange + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + t.Run("Resource with InclusiveRange", func(t *testing.T) { t.Parallel() @@ -667,6 +705,44 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) }) + t.Run("Contract with ResourceInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + resource interface Foo { + fun getRange(): InclusiveRange + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with ResourceInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + resource interface Foo { + fun getRange(): InclusiveRange + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + t.Run("Type with InclusiveRange", func(t *testing.T) { t.Parallel() From 90ede500b071ac1a7471f3450f2b1336c3ba239b Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 5 Nov 2023 19:58:53 +0530 Subject: [PATCH 06/13] Fix typo for lint --- runtime/tests/checker/typeargument_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 5c6d242edf..a2204e167a 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -486,7 +486,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { errs := RequireCheckerErrors(t, err, 2) - // 2 errors for the two occurences of InclusiveRange. + // 2 errors for the two occurrences of InclusiveRange. assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[1]) }) From 141012f82c5295e22964507ce477b83d93592424 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 9 Nov 2023 22:49:08 +0530 Subject: [PATCH 07/13] Move instantiation checks to Type interface --- runtime/sema/checker.go | 91 +----------------------- runtime/sema/simple_type.go | 3 + runtime/sema/type.go | 137 ++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 90 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 8d3d2e1ac9..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,95 +2068,6 @@ func (checker *Checker) checkInvalidInterfaceAsType(ty Type, pos ast.HasPosition } } -func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.HasPosition) { - switch t := ty.(type) { - case *OptionalType: - checker.checkParameterizedTypeIsInstantiated(t.Type, pos) - - case ArrayType: - checker.checkParameterizedTypeIsInstantiated(t.ElementType(false), pos) - - case *DictionaryType: - checker.checkParameterizedTypeIsInstantiated(t.KeyType, pos) - checker.checkParameterizedTypeIsInstantiated(t.ValueType, pos) - - case *ReferenceType: - checker.checkParameterizedTypeIsInstantiated(t.Type, pos) - - case *FunctionType: - for _, tyParam := range t.TypeParameters { - checker.checkParameterizedTypeIsInstantiated(tyParam.TypeBound, pos) - } - - for _, param := range t.Parameters { - checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) - } - - checker.checkParameterizedTypeIsInstantiated(t.ReturnTypeAnnotation.Type, pos) - - case *RestrictedType: - checker.checkParameterizedTypeIsInstantiated(t.Type, pos) - - case *CompositeType: - if t.EnumRawType != nil { - checker.checkParameterizedTypeIsInstantiated(t.EnumRawType, pos) - } - if t.baseType != nil { - checker.checkParameterizedTypeIsInstantiated(t.baseType, pos) - } - - for _, typ := range t.ImplicitTypeRequirementConformances { - checker.checkParameterizedTypeIsInstantiated(typ, pos) - } - - for _, typ := range t.ExplicitInterfaceConformances { - checker.checkParameterizedTypeIsInstantiated(typ, pos) - } - - case *InterfaceType: - for _, param := range t.InitializerParameters { - checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) - } - - case ParameterizedType: - typeArgs := t.TypeArguments() - typeParameters := t.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..27d0c5442f 100644 --- a/runtime/sema/simple_type.go +++ b/runtime/sema/simple_type.go @@ -168,3 +168,6 @@ func (t *SimpleType) CompositeKind() common.CompositeKind { return common.CompositeKindStructure } } + +func (t *SimpleType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +} diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 20db7a64cc..ee88cd8b31 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,49 @@ 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() + + typeArgumentCount := len(typeArgs) + typeParameterCount := len(typeParameters) + + if typeArgumentCount != typeParameterCount { + report( + &InvalidTypeArgumentCountError{ + TypeParameterCount: typeParameterCount, + TypeArgumentCount: typeArgumentCount, + Range: ast.NewRange( + memoryGauge, + pos.StartPosition(), + pos.EndPosition(memoryGauge), + ), + }, + ) + } + + // 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 +739,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 +952,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 +1229,9 @@ func (t *NumericType) IsSuperType() bool { return t.isSuperType } +func (*NumericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +} + // FixedPointNumericType represents all the types in the fixed-point range. type FixedPointNumericType struct { maxFractional *big.Int @@ -1369,6 +1425,9 @@ func (t *FixedPointNumericType) IsSuperType() bool { return t.isSuperType } +func (*FixedPointNumericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +} + // Numeric types var ( @@ -2551,6 +2610,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 +2770,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 +3439,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 +4441,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 +4945,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 +5080,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 +5556,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 +5918,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 +6017,9 @@ func (t *AddressType) Resolve(_ *TypeParameterTypeOrderedMap) Type { return t } +func (*AddressType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +} + const AddressTypeToBytesFunctionName = `toBytes` var AddressTypeToBytesFunctionType = &FunctionType{ @@ -6816,6 +6935,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 +7250,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 +7457,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 From 460533d060f7c7e7b49848036c12d398f8e3209f Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 10 Nov 2023 21:13:24 +0530 Subject: [PATCH 08/13] Declare and use a test function --- runtime/tests/checker/typeargument_test.go | 122 ++++++++------------- 1 file changed, 46 insertions(+), 76 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index a2204e167a..64981bba15 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -159,21 +159,25 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) - baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) - options := ParseAndCheckOptions{ - Config: &sema.Config{ - BaseValueActivation: baseValueActivation, - }, + 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 := ParseAndCheckWithOptions(t, + err := test(t, "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", - options, ) require.NoError(t, err) @@ -183,9 +187,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -197,9 +200,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: [InclusiveRange] = []", - options, ) require.NoError(t, err) @@ -209,9 +211,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: [InclusiveRange] = []", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -223,9 +224,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", - options, ) require.NoError(t, err) @@ -235,9 +235,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -249,9 +248,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: InclusiveRange? = nil", - options, ) require.NoError(t, err) @@ -261,9 +259,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: InclusiveRange? = nil", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -275,9 +272,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: {Int: InclusiveRange} = {}", - options, ) require.NoError(t, err) @@ -287,9 +283,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: {Int: InclusiveRange} = {}", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -301,7 +296,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Foo { let a: InclusiveRange @@ -311,7 +306,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -321,7 +315,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Foo { let a: InclusiveRange @@ -331,7 +325,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -343,7 +336,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Bar { let a: InclusiveRange @@ -361,7 +354,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -371,7 +363,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Bar { let a: InclusiveRange @@ -389,7 +381,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -401,7 +392,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { struct Foo { @@ -413,7 +404,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -423,7 +413,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { struct Foo { @@ -435,7 +425,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -447,7 +436,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Bar { let f: ((): InclusiveRange) @@ -459,7 +448,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -469,7 +457,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Bar { let f: ((): InclusiveRange) @@ -481,7 +469,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 2) @@ -495,13 +482,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct interface Bar { fun getRange(): InclusiveRange } `, - options, ) require.NoError(t, err) @@ -511,13 +497,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct interface Bar { fun getRange(): InclusiveRange } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -529,7 +514,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource Foo { let a: InclusiveRange @@ -539,7 +524,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -549,7 +533,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { struct interface Foo { @@ -557,7 +541,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -567,7 +550,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { struct interface Foo { @@ -575,7 +558,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -587,7 +569,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource Foo { let a: InclusiveRange @@ -597,7 +579,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -609,7 +590,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource Bar { let a: InclusiveRange @@ -631,7 +612,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -641,7 +621,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource Bar { let a: InclusiveRange @@ -663,7 +643,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -675,13 +654,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource interface Bar { fun getRange(): InclusiveRange } `, - options, ) require.NoError(t, err) @@ -691,13 +669,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource interface Bar { fun getRange(): InclusiveRange } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -709,7 +686,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { resource interface Foo { @@ -717,7 +694,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -727,7 +703,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { resource interface Foo { @@ -735,7 +711,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -747,13 +722,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` pub fun main(): Type { return Type>() } `, - options, ) require.NoError(t, err) @@ -763,13 +737,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` pub fun main(): Type { return Type() } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -781,9 +754,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "event Foo(bar: InclusiveRange)", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -795,9 +767,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "event Foo(bar: InclusiveRange)", - options, ) errs := RequireCheckerErrors(t, err, 2) @@ -810,7 +781,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` pub fun main(): Direction { return Direction.RIGHT @@ -823,7 +794,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { pub case RIGHT } `, - options, ) require.NoError(t, err) From 21c83f87cec47d57ac0f2662e436e9045ddbf442 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 10 Nov 2023 21:16:58 +0530 Subject: [PATCH 09/13] Add NO-OP comments and replace unused parameters with _ --- runtime/sema/simple_type.go | 3 ++- runtime/sema/type.go | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/sema/simple_type.go b/runtime/sema/simple_type.go index 27d0c5442f..c9ea9941bf 100644 --- a/runtime/sema/simple_type.go +++ b/runtime/sema/simple_type.go @@ -169,5 +169,6 @@ func (t *SimpleType) CompositeKind() common.CompositeKind { } } -func (t *SimpleType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +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 ee88cd8b31..dae4e2d9c2 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -1229,7 +1229,8 @@ func (t *NumericType) IsSuperType() bool { return t.isSuperType } -func (*NumericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +func (*NumericType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP } // FixedPointNumericType represents all the types in the fixed-point range. @@ -1425,7 +1426,8 @@ func (t *FixedPointNumericType) IsSuperType() bool { return t.isSuperType } -func (*FixedPointNumericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +func (*FixedPointNumericType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP } // Numeric types @@ -6017,7 +6019,8 @@ func (t *AddressType) Resolve(_ *TypeParameterTypeOrderedMap) Type { return t } -func (*AddressType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +func (*AddressType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP } const AddressTypeToBytesFunctionName = `toBytes` From f2adc9173c94f02c12d81f26fc79f2b5c718ca5a Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 2 Dec 2023 15:34:11 +0530 Subject: [PATCH 10/13] Remove redundant check --- runtime/sema/type.go | 17 +---------------- runtime/tests/checker/typeargument_test.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index dae4e2d9c2..6343f6e6aa 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -304,22 +304,7 @@ func CheckParameterizedTypeInstantiated( typeArgs := t.TypeArguments() typeParameters := t.TypeParameters() - typeArgumentCount := len(typeArgs) - typeParameterCount := len(typeParameters) - - if typeArgumentCount != typeParameterCount { - report( - &InvalidTypeArgumentCountError{ - TypeParameterCount: typeParameterCount, - TypeArgumentCount: typeArgumentCount, - Range: ast.NewRange( - memoryGauge, - pos.StartPosition(), - pos.EndPosition(memoryGauge), - ), - }, - ) - } + // 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 { diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 64981bba15..ba193cb82a 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -183,6 +183,20 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { 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() From a5beda930355096a3487c43e73b0545250fcc685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 7 Dec 2023 10:59:16 -0800 Subject: [PATCH 11/13] add tests for functrion types --- runtime/tests/checker/typeargument_test.go | 140 ++++++++++++++++++++- 1 file changed, 138 insertions(+), 2 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index ba193cb82a..0a23b4da87 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" ) @@ -812,6 +813,141 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { 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) { @@ -904,7 +1040,7 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { `, &sema.CapabilityType{}, ) - require.NotNil(t, checker) + require.NoError(t, err) capType := RequireGlobalValue(t, checker.Elaboration, "cap") require.IsType(t, @@ -945,7 +1081,7 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { }, }, ) - require.NotNil(t, checker) + require.NoError(t, err) assert.Equal(t, &sema.CapabilityType{ From 3bd5990eaa54abb22331a5f887c1af9c4ac082e4 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 8 Dec 2023 21:28:37 +0530 Subject: [PATCH 12/13] Use function instead of sharing object --- runtime/tests/checker/range_value_test.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) 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]) From f82c07fcd901217528fd10ffcccb916f4d2b2303 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 8 Dec 2023 13:58:36 -0800 Subject: [PATCH 13/13] Remove unnecessary variable-type assertion --- runtime/tests/checker/typeargument_test.go | 45 +--------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 0a23b4da87..6fd00c22fb 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -1033,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.NoError(t, err) - - 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]) }) @@ -1070,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 @@ -1081,28 +1060,8 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { }, }, ) - require.NoError(t, err) - - 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]) }) }