diff --git a/runtime/sema/check_reference_expression.go b/runtime/sema/check_reference_expression.go index 88b62af78c..477674e2f8 100644 --- a/runtime/sema/check_reference_expression.go +++ b/runtime/sema/check_reference_expression.go @@ -71,7 +71,16 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere referencedExpression := referenceExpression.Expression - _, _ = checker.visitExpression(referencedExpression, targetType) + referencedType, _ := checker.visitExpression(referencedExpression, targetType) + + if !IsValidReferencedType(referencedType) { + checker.report( + &InvalidReferenceTargetTypeError{ + TargetType: targetType, + Range: ast.NewRangeFromPositioned(checker.memoryGauge, referenceExpression.Expression), + }, + ) + } if referenceType == nil { return InvalidType @@ -81,3 +90,13 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere return returnType } + +func IsValidReferencedType(referencedType Type) bool { + switch referencedType.(type) { + case *TransactionRoleType: + return false + // TODO: in Stable Cadence we may also want to disallow transactions + } + + return true +} diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 8aed019865..b9b21ac17e 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2233,16 +2233,16 @@ func (checker *Checker) VisitExpressionWithForceType(expr ast.Expression, expect // visitExpressionWithForceType // // Parameters: -// expr - Expression to check -// expectedType - Contextually expected type of the expression -// forceType - Specifies whether to use the expected type as a hard requirement (forceType = true) -// -// or whether to use the expected type for type inferring only (forceType = false) +// - expr: Expression to check +// - expectedType: Contextually expected type of the expression +// - forceType: +// Specifies whether to use the expected type as a hard requirement (forceType = true) +// or whether to use the expected type for type inferring only (forceType = false) // // Return types: -// visibleType - The type that others should 'see' as the type of this expression. This could be -// -// used as the type of the expression to avoid the type errors being delegated up. +// - visibleType: +// The type that others should 'see' as the type of this expression. +// This could be used as the type of the expression to avoid the type errors being delegated up. // // actualType - The actual type of the expression. func (checker *Checker) visitExpressionWithForceType( diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 0b5bbb7c9a..f9632ce00f 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -2615,6 +2615,27 @@ func (e *NonReferenceTypeReferenceError) SecondaryError() string { ) } +// InvalidReferenceTargetTypeError is reported when a reference expression +// targets a type which may not be referenced. +type InvalidReferenceTargetTypeError struct { + TargetType Type + ast.Range +} + +var _ SemanticError = &InvalidReferenceTargetTypeError{} +var _ errors.UserError = &InvalidReferenceTargetTypeError{} + +func (*InvalidReferenceTargetTypeError) isSemanticError() {} + +func (*InvalidReferenceTargetTypeError) IsUserError() {} + +func (e *InvalidReferenceTargetTypeError) Error() string { + return fmt.Sprintf( + "cannot create reference to `%s`", + e.TargetType.QualifiedString(), + ) +} + // InvalidResourceCreationError type InvalidResourceCreationError struct { @@ -2903,7 +2924,6 @@ func (e *ReadOnlyTargetAssignmentError) Error() string { } // MissingPrepareForFieldError -// type MissingPrepareForFieldError struct { FirstFieldName string FirstFieldPos ast.Position diff --git a/runtime/tests/checker/reference_test.go b/runtime/tests/checker/reference_test.go index 2a401971c4..da2574f1a6 100644 --- a/runtime/tests/checker/reference_test.go +++ b/runtime/tests/checker/reference_test.go @@ -1279,3 +1279,28 @@ func TestCheckReferenceTypeImplicitConformance(t *testing.T) { require.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) } + +func TestCheckInvalidReferenceTargetType(t *testing.T) { + + t.Parallel() + + t.Run("transaction role", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + transaction { + + role role1 {} + + execute { + &self.role1 as &AnyStruct + } + } + `) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.InvalidReferenceTargetTypeError{}, errs[0]) + }) +}