Skip to content

Commit

Permalink
Reject reference to optionals in the checker
Browse files Browse the repository at this point in the history
  • Loading branch information
SupunS committed Feb 27, 2024
1 parent 81e9c86 commit 39d2cc0
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 19 deletions.
14 changes: 13 additions & 1 deletion runtime/sema/check_reference_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,24 @@ func (checker *Checker) expectedTypeForReferencedExpr(

switch expectedType := expectedType.(type) {
case *OptionalType:
expectedLeftType, returnType, referenceType = checker.expectedTypeForReferencedExpr(expectedType.Type, hasPosition)
expectedLeftType, returnType, referenceType =
checker.expectedTypeForReferencedExpr(expectedType.Type, hasPosition)

// Re-wrap with an optional
expectedLeftType = &OptionalType{Type: expectedLeftType}
returnType = &OptionalType{Type: returnType}

case *ReferenceType:
referencedType := expectedType.Type
if referencedOptionalType, referenceToOptional := referencedType.(*OptionalType); referenceToOptional {
checker.report(
&ReferenceToAnOptionalError{
ReferencedOptionalType: referencedOptionalType,
Range: ast.NewRangeFromPositioned(checker.memoryGauge, hasPosition),
},
)
}

return expectedType.Type, expectedType, expectedType

default:
Expand Down
36 changes: 36 additions & 0 deletions runtime/sema/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2795,6 +2795,42 @@ func (e *NonReferenceTypeReferenceError) SecondaryError() string {
)
}

// ReferenceToAnOptionalError

type ReferenceToAnOptionalError struct {
ReferencedOptionalType *OptionalType
ast.Range
}

var _ SemanticError = &ReferenceToAnOptionalError{}
var _ errors.UserError = &ReferenceToAnOptionalError{}
var _ errors.SecondaryError = &ReferenceToAnOptionalError{}

func (*ReferenceToAnOptionalError) isSemanticError() {}

func (*ReferenceToAnOptionalError) IsUserError() {}

func (e *ReferenceToAnOptionalError) Error() string {
return "cannot create reference"
}

func (e *ReferenceToAnOptionalError) SecondaryError() string {
return fmt.Sprintf(
"expected non-optional type, got `%s`. Consider taking a reference with type `%s`",
e.ReferencedOptionalType.QualifiedString(),

// Suggest taking the optional out of the reference type.
NewOptionalType(
nil,
NewReferenceType(
nil,
UnauthorizedAccess,
e.ReferencedOptionalType.Type,
),
),
)
}

// InvalidResourceCreationError

type InvalidResourceCreationError struct {
Expand Down
19 changes: 18 additions & 1 deletion runtime/tests/checker/reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3823,6 +3823,22 @@ func TestCheckOptionalReference(t *testing.T) {
require.NoError(t, err)
})

t.Run("reference to optional", func(t *testing.T) {
t.Parallel()

_, err := ParseAndCheck(t, `
fun main() {
var dict: {String: Foo} = {}
var ref: &(Foo?) = &dict["foo"] as &(Foo?)
}
struct Foo {}
`)

errs := RequireCheckerErrors(t, err, 1)
assert.IsType(t, &sema.ReferenceToAnOptionalError{}, errs[0])
})

t.Run("reference to nested optional", func(t *testing.T) {
t.Parallel()

Expand All @@ -3835,6 +3851,7 @@ func TestCheckOptionalReference(t *testing.T) {
struct Foo {}
`)

require.NoError(t, err)
errs := RequireCheckerErrors(t, err, 1)
assert.IsType(t, &sema.ReferenceToAnOptionalError{}, errs[0])
})
}
19 changes: 2 additions & 17 deletions runtime/tests/interpreter/reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3197,23 +3197,8 @@ func TestInterpretOptionalReference(t *testing.T) {
inter := parseCheckAndInterpret(t, `
fun main() {
var dict: {String: Foo?} = {}
var ref: (&Foo)?? = &dict["foo"] as &Foo??
}
struct Foo {}
`)

_, err := inter.Invoke("main")
require.NoError(t, err)
})

t.Run("reference to nested optional", func(t *testing.T) {
t.Parallel()

inter := parseCheckAndInterpret(t, `
fun main() {
var dict: {String: Foo?} = {}
var ref: &(Foo??) = &dict["foo"] as &(Foo??)
var element: Foo?? = dict["foo"]
var ref: &(Foo)?? = &element as &(Foo)??
}
struct Foo {}
Expand Down

0 comments on commit 39d2cc0

Please sign in to comment.