Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow dereferencing references to containers of non-resources #3034

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions runtime/sema/check_unary_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,11 @@ func (checker *Checker) VisitUnaryExpression(expression *ast.UnaryExpression) Ty

innerType := referenceType.Type

// Allow primitives or containers of primitives.
if !IsPrimitiveOrContainerOfPrimitive(innerType) {
if !IsPrimitiveOrNonResourceContainer(innerType) {
checker.report(
&InvalidUnaryOperandError{
Operation: expression.Operation,
ExpectedTypeDescription: "primitive or container of primitives",
ExpectedTypeDescription: "primitive or non-resource container",
ActualType: innerType,
Range: ast.NewRangeFromPositioned(
checker.memoryGauge,
Expand Down
13 changes: 5 additions & 8 deletions runtime/sema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -7059,16 +7059,13 @@ func (t *AddressType) initializeMemberResolvers() {
})
}

func IsPrimitiveOrContainerOfPrimitive(ty Type) bool {
switch ty := ty.(type) {
case *VariableSizedType:
return IsPrimitiveOrContainerOfPrimitive(ty.Type)

case *ConstantSizedType:
return IsPrimitiveOrContainerOfPrimitive(ty.Type)
func IsPrimitiveOrNonResourceContainer(referencedType Type) bool {
switch ty := referencedType.(type) {
case ArrayType:
return !ty.IsResourceType()

case *DictionaryType:
return IsPrimitiveOrContainerOfPrimitive(ty.ValueType)
return !ty.IsResourceType()

default:
return ty.IsPrimitiveType()
Expand Down
133 changes: 88 additions & 45 deletions runtime/tests/checker/reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3139,12 +3139,9 @@ func TestCheckDereference(t *testing.T) {

require.NoError(t, err)

yType := RequireGlobalValue(t, checker.Elaboration, "y")
derefType := RequireGlobalValue(t, checker.Elaboration, "deref")

assert.Equal(t,
expectedTy,
yType,
)
assert.True(t, expectedTy.Equal(derefType))
})
}

Expand All @@ -3171,8 +3168,8 @@ func TestCheckDereference(t *testing.T) {
typString,
fmt.Sprintf(
`
let x: &%[1]s = &1
let y: %[1]s = *x
let ref: &%[1]s = &1
let deref: %[1]s = *ref
`,
integerType,
),
Expand All @@ -3189,8 +3186,8 @@ func TestCheckDereference(t *testing.T) {
typString,
fmt.Sprintf(
`
let x: &%[1]s = &1.0
let y: %[1]s = *x
let ref: &%[1]s = &1.0
let deref: %[1]s = *ref
`,
fixedPointType,
),
Expand All @@ -3205,11 +3202,11 @@ func TestCheckDereference(t *testing.T) {
for _, testCase := range []testCase{
{
ty: sema.CharacterType,
initializer: "\"\\u{FC}\"",
initializer: `"\u{FC}"`,
},
{
ty: sema.StringType,
initializer: "\"\\u{FC}\"",
initializer: `"\u{FC}"`,
},
{
ty: sema.BoolType,
Expand All @@ -3234,8 +3231,8 @@ func TestCheckDereference(t *testing.T) {
fmt.Sprintf(
`
let value: %[1]s = %[2]s
let x: &%[1]s = &value
let y: %[1]s = *x
let ref: &%[1]s = &value
let deref: %[1]s = *ref
`,
testCase.ty,
testCase.initializer,
Expand All @@ -3259,23 +3256,23 @@ func TestCheckDereference(t *testing.T) {
},
{
ty: &sema.VariableSizedType{Type: sema.StringType},
initializer: "[\"abc\", \"def\"]",
initializer: `["abc", "def"]`,
},
{
ty: &sema.VariableSizedType{
Type: &sema.VariableSizedType{
Type: sema.StringType,
},
},
initializer: "[ [\"abc\", \"def\"], [\"xyz\"]]",
initializer: `[ ["abc", "def"], ["xyz"]]`,
},
{
ty: &sema.VariableSizedType{
Type: &sema.DictionaryType{
KeyType: sema.IntType,
ValueType: sema.StringType,
}},
initializer: "[{1: \"abc\", 2: \"def\"}, {3: \"xyz\"}]",
initializer: `[{1: "abc", 2: "def"}, {3: "xyz"}]`,
},
{
ty: &sema.ConstantSizedType{Type: sema.IntType, Size: 3},
Expand All @@ -3287,7 +3284,7 @@ func TestCheckDereference(t *testing.T) {
},
{
ty: &sema.ConstantSizedType{Type: sema.StringType, Size: 2},
initializer: "[\"abc\", \"def\"]",
initializer: `["abc", "def"]`,
},
{
ty: &sema.ConstantSizedType{
Expand All @@ -3296,7 +3293,7 @@ func TestCheckDereference(t *testing.T) {
},
Size: 2,
},
initializer: "[ [\"abc\", \"def\"], [\"xyz\"]]",
initializer: `[ ["abc", "def"], ["xyz"]]`,
},
{
ty: &sema.ConstantSizedType{
Expand All @@ -3306,17 +3303,40 @@ func TestCheckDereference(t *testing.T) {
},
Size: 1,
},
initializer: "[{1: \"abc\", 2: \"def\"}]",
initializer: `[{1: "abc", 2: "def"}]`,
},
{
ty: &sema.VariableSizedType{
Type: &sema.CompositeType{
Kind: common.CompositeKindStructure,
Location: utils.TestLocation,
Identifier: "S",
},
},
initializer: `[S(), S()]`,
},
{
ty: &sema.ConstantSizedType{
Type: &sema.CompositeType{
Kind: common.CompositeKindStructure,
Location: utils.TestLocation,
Identifier: "S",
},
Size: 2,
},
initializer: `[S(), S()]`,
},
} {
runValidTestCase(
t,
testCase.ty.QualifiedString(),
fmt.Sprintf(
`
struct S {}

let value: %[1]s = %[2]s
let x: &%[1]s = &value
let y: %[1]s = *x
let ref: &%[1]s = &value
let deref: %[1]s = *ref
`,
testCase.ty,
testCase.initializer,
Expand All @@ -3325,34 +3345,39 @@ func TestCheckDereference(t *testing.T) {
)
}

// Arrays of non-primitives cannot be dereferenced.
// Arrays of resources cannot be dereferenced.
runInvalidTestCase(
t,
"[Struct]",
"[Resource]",
`
struct S{}
resource R {}

fun test() {
let value: [S] = [S(), S()]
let x: &[S] = &value
let y: [S] = *x
let array: @[R] <- [<-create R(), <-create R()]
let ref: &[R] = &array
let deref: @[R] <- *ref
destroy array
destroy deref
}
`,
)

runInvalidTestCase(
t,
"[Struct; 3]",
"[Resource; 2]",
`
struct S{}
resource R {}

fun test() {
let value: [S; 3] = [S(),S(),S()]
let x: &[S; 3] = &value
let y: [S; 3] = *x
let array: @[R; 2] <- [<-create R(), <-create R()]
let ref: &[R; 2] = &array
let deref: @[R; 2] <- *ref
destroy array
destroy deref
}
`,
)

})

t.Run("Dictionary", func(t *testing.T) {
Expand All @@ -3369,7 +3394,7 @@ func TestCheckDereference(t *testing.T) {
},
{
ty: &sema.DictionaryType{KeyType: sema.StringType, ValueType: sema.StringType},
initializer: "{\"123\": \"abc\", \"456\": \"def\"}",
initializer: `{"123": "abc", "456": "def"}`,
},
{
ty: &sema.DictionaryType{
Expand All @@ -3378,7 +3403,7 @@ func TestCheckDereference(t *testing.T) {
Type: sema.IntType,
},
},
initializer: "{\"123\": [1, 2, 3], \"456\": [4, 5, 6]}",
initializer: `{"123": [1, 2, 3], "456": [4, 5, 6]}`,
},
{
ty: &sema.DictionaryType{
Expand All @@ -3388,17 +3413,30 @@ func TestCheckDereference(t *testing.T) {
Size: 3,
},
},
initializer: "{\"123\": [1, 2, 3], \"456\": [4, 5, 6]}",
initializer: `{"123": [1, 2, 3], "456": [4, 5, 6]}`,
},
{
ty: &sema.DictionaryType{
KeyType: sema.IntType,
ValueType: &sema.CompositeType{
Kind: common.CompositeKindStructure,
Location: utils.TestLocation,
Identifier: "S",
},
},
initializer: `{1: S(), 2: S()}`,
},
} {
runValidTestCase(
t,
testCase.ty.QualifiedString(),
fmt.Sprintf(
`
struct S {}

let value: %[1]s = %[2]s
let x: &%[1]s = &value
let y: %[1]s = *x
let ref: &%[1]s = &value
let deref: %[1]s = *ref
`,
testCase.ty,
testCase.initializer,
Expand All @@ -3407,17 +3445,22 @@ func TestCheckDereference(t *testing.T) {
)
}

// Dictionaries with value as non-primitive cannot be dereferenced.
// Dictionaries of resources cannot be dereferenced.
runInvalidTestCase(
t,
"{Int: Struct}",
"{Int: Resource}",
`
struct S{}
resource R {}

fun test() {
let value: {Int: S} = { 1: S(), 2: S() }
let x: &{Int: S} = &value
let y: {Int: S} = *x
let dict: @{Int: R} <- {
1: <-create R(),
2: <-create R()
}
let ref: &{Int: R} = &dict
let deref: @{Int: R} <- *ref
destroy dict
destroy deref
}
`,
)
Expand Down Expand Up @@ -3449,7 +3492,7 @@ func TestCheckDereference(t *testing.T) {
t,
"Struct",
`
struct S{}
struct S {}

fun test() {
let s = S()
Expand Down Expand Up @@ -3482,7 +3525,7 @@ func TestCheckDereference(t *testing.T) {
"valid",
`
let ref: &Int? = &1 as &Int
let y = *ref
let deref = *ref
`,
&sema.OptionalType{
Type: sema.IntType,
Expand Down
Loading
Loading