From d962c073e9ad86c77eebc52ae1c812778fe047c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 8 Feb 2024 18:25:39 -0800 Subject: [PATCH 1/3] allow contract interface types in intersection types --- runtime/sema/checker.go | 11 ++- runtime/tests/checker/intersection_test.go | 103 +++++++++++++++++++++ 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index a049b545cf..adf9620518 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -982,11 +982,15 @@ func CheckIntersectionType( // the type is ambiguous. report(func(t *ast.IntersectionType) error { - return &AmbiguousIntersectionTypeError{Range: ast.NewRangeFromPositioned(memoryGauge, t)} + return &AmbiguousIntersectionTypeError{ + Range: ast.NewRangeFromPositioned(memoryGauge, t), + } }) return InvalidType - case common.CompositeKindResource, common.CompositeKindStructure: + case common.CompositeKindResource, + common.CompositeKindStructure, + common.CompositeKindContract: break default: @@ -1011,8 +1015,7 @@ func (checker *Checker) convertIntersectionType(t *ast.IntersectionType) Type { if ok { intersectedCompositeKind = intersectedInterfaceType.CompositeKind } - if !ok || (intersectedCompositeKind != common.CompositeKindResource && - intersectedCompositeKind != common.CompositeKindStructure) { + if !ok || !intersectedCompositeKind.SupportsInterfaces() { if !intersectedResult.IsInvalidType() { checker.report(&InvalidIntersectedTypeError{ diff --git a/runtime/tests/checker/intersection_test.go b/runtime/tests/checker/intersection_test.go index d3395b740c..33ce597d07 100644 --- a/runtime/tests/checker/intersection_test.go +++ b/runtime/tests/checker/intersection_test.go @@ -264,6 +264,109 @@ func TestCheckIntersectionType(t *testing.T) { assert.IsType(t, &sema.AmbiguousIntersectionTypeError{}, errs[0]) }) + + t.Run("contract interface", func(t *testing.T) { + + _, err := ParseAndCheck(t, ` + contract interface CI {} + + fun test (_ c: {CI}) {} + `) + + require.NoError(t, err) + }) + + t.Run("contract", func(t *testing.T) { + + _, err := ParseAndCheck(t, ` + contract C {} + + fun test (_ c: {C}) {} + `) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.InvalidIntersectedTypeError{}, errs[0]) + // Checker is not able to infer valid composite kind based on intersection's types + assert.IsType(t, &sema.AmbiguousIntersectionTypeError{}, errs[1]) + }) + + t.Run("enum", func(t *testing.T) { + + _, err := ParseAndCheck(t, ` + enum E: UInt8 {} + + fun test (_ e: {E}) {} + `) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.InvalidIntersectedTypeError{}, errs[0]) + // Checker is not able to infer valid composite kind based on intersection's types + assert.IsType(t, &sema.AmbiguousIntersectionTypeError{}, errs[1]) + }) + + t.Run("event", func(t *testing.T) { + + _, err := ParseAndCheck(t, ` + event E() + + fun test (_ e: {E}) {} + `) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.InvalidIntersectedTypeError{}, errs[0]) + // Checker is not able to infer valid composite kind based on intersection's types + assert.IsType(t, &sema.AmbiguousIntersectionTypeError{}, errs[1]) + }) + + t.Run("attachment", func(t *testing.T) { + + _, err := ParseAndCheck(t, ` + resource R {} + + attachment A for R {} + + fun test (_ a: {A}) {} + `) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.InvalidIntersectedTypeError{}, errs[0]) + // Checker is not able to infer valid composite kind based on intersection's types + assert.IsType(t, &sema.AmbiguousIntersectionTypeError{}, errs[1]) + }) + + t.Run("entitlement", func(t *testing.T) { + + _, err := ParseAndCheck(t, ` + entitlement E + + fun test (_ e: {E}) {} + `) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.InvalidIntersectedTypeError{}, errs[0]) + // Checker is not able to infer valid composite kind based on intersection's types + assert.IsType(t, &sema.AmbiguousIntersectionTypeError{}, errs[1]) + }) + + t.Run("entitlement mapping", func(t *testing.T) { + + _, err := ParseAndCheck(t, ` + entitlement mapping M {} + + fun test (_ m: {M}) {} + `) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.InvalidIntersectedTypeError{}, errs[0]) + // Checker is not able to infer valid composite kind based on intersection's types + assert.IsType(t, &sema.AmbiguousIntersectionTypeError{}, errs[1]) + }) } func TestCheckIntersectionTypeMemberAccess(t *testing.T) { From 66f24bac0eb19e10030f6afe473b65dc08d4323d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 8 Feb 2024 18:32:23 -0800 Subject: [PATCH 2/3] improve error message --- runtime/sema/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 3b43f95eaa..5159e42e73 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -3616,7 +3616,7 @@ func (*InvalidIntersectedTypeError) IsUserError() {} func (e *InvalidIntersectedTypeError) Error() string { return fmt.Sprintf( - "cannot restrict using non-resource/structure interface type: `%s`", + "cannot restrict using non-resource/structure/contract interface type: `%s`", e.Type.QualifiedString(), ) } From e7de6ad532b3551d3e8fdbf396017beefb3af106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 8 Feb 2024 18:33:03 -0800 Subject: [PATCH 3/3] remove unused error, it is no longer needed --- runtime/sema/errors.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 5159e42e73..17299b4870 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -3665,27 +3665,6 @@ func (e *InvalidIntersectionTypeDuplicateError) Error() string { ) } -// InvalidNonConformanceIntersectionError - -type InvalidNonConformanceIntersectionError struct { - Type *InterfaceType - ast.Range -} - -var _ SemanticError = &InvalidNonConformanceIntersectionError{} -var _ errors.UserError = &InvalidNonConformanceIntersectionError{} - -func (*InvalidNonConformanceIntersectionError) isSemanticError() {} - -func (*InvalidNonConformanceIntersectionError) IsUserError() {} - -func (e *InvalidNonConformanceIntersectionError) Error() string { - return fmt.Sprintf( - "intersection type does not conform to restricting type: `%s`", - e.Type.QualifiedString(), - ) -} - // IntersectionMemberClashError type IntersectionMemberClashError struct {