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

Make indexing assignment entitled #2664

Merged
merged 10 commits into from
Aug 2, 2023
4 changes: 2 additions & 2 deletions runtime/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1480,7 +1480,7 @@ func TestRuntimePublicKey(t *testing.T) {
signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
)

var publickeyRef = &publicKey.publicKey as &[UInt8]
var publickeyRef = &publicKey.publicKey as auth(Mutable) &[UInt8]
publickeyRef[0] = 3

return publicKey
Expand Down Expand Up @@ -1909,7 +1909,7 @@ func TestAuthAccountContracts(t *testing.T) {
script := []byte(`
transaction {
prepare(signer: AuthAccount) {
var namesRef = &signer.contracts.names as &[String]
var namesRef = &signer.contracts.names as auth(Mutable) &[String]
namesRef[0] = "baz"

assert(signer.contracts.names[0] == "foo")
Expand Down
19 changes: 10 additions & 9 deletions runtime/capabilitycontrollers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {
/// > Our version of quicksort is not the fastest possible,
/// > but it's one of the simplest.
///
access(all) fun quickSort(_ items: &[AnyStruct], isLess: fun(Int, Int): Bool) {
access(all) fun quickSort(_ items: auth(Mutable) &[AnyStruct], isLess: fun(Int, Int): Bool) {

fun quickSortPart(leftIndex: Int, rightIndex: Int) {

Expand All @@ -95,6 +95,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {

let pivotIndex = (leftIndex + rightIndex) / 2

items[pivotIndex] <-> items[leftIndex]
items[pivotIndex] <-> items[leftIndex]

var lastIndex = leftIndex
Expand Down Expand Up @@ -1802,7 +1803,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {
assert(controllers1.length == 3)

Test.quickSort(
&controllers1 as &[AnyStruct],
&controllers1 as auth(Mutable) &[AnyStruct],
isLess: fun(i: Int, j: Int): Bool {
let a = controllers1[i]
let b = controllers1[j]
Expand Down Expand Up @@ -1900,7 +1901,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {
assert(controllers1.length == 3)

Test.quickSort(
&controllers1 as &[AnyStruct],
&controllers1 as auth(Mutable) &[AnyStruct],
isLess: fun(i: Int, j: Int): Bool {
let a = controllers1[i]
let b = controllers1[j]
Expand Down Expand Up @@ -2251,7 +2252,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {
assert(controllers.length == 3)

Test.quickSort(
&controllers as &[AnyStruct],
&controllers as auth(Mutable) &[AnyStruct],
isLess: fun(i: Int, j: Int): Bool {
let a = controllers[i]
let b = controllers[j]
Expand Down Expand Up @@ -2329,7 +2330,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {
assert(controllers.length == 3)

Test.quickSort(
&controllers as &[AnyStruct],
&controllers as auth(Mutable) &[AnyStruct],
isLess: fun(i: Int, j: Int): Bool {
let a = controllers[i]
let b = controllers[j]
Expand Down Expand Up @@ -2584,7 +2585,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {

let controllers1Before = signer.capabilities.storage.getControllers(forPath: storagePath1)
Test.quickSort(
&controllers1Before as &[AnyStruct],
&controllers1Before as auth(Mutable) &[AnyStruct],
isLess: fun(i: Int, j: Int): Bool {
let a = controllers1Before[i]
let b = controllers1Before[j]
Expand All @@ -2598,7 +2599,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {

let controllers2Before = signer.capabilities.storage.getControllers(forPath: storagePath2)
Test.quickSort(
&controllers2Before as &[AnyStruct],
&controllers2Before as auth(Mutable) &[AnyStruct],
isLess: fun(i: Int, j: Int): Bool {
let a = controllers2Before[i]
let b = controllers2Before[j]
Expand All @@ -2622,7 +2623,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {

let controllers1After = signer.capabilities.storage.getControllers(forPath: storagePath1)
Test.quickSort(
&controllers1After as &[AnyStruct],
&controllers1After as auth(Mutable) &[AnyStruct],
isLess: fun(i: Int, j: Int): Bool {
let a = controllers1After[i]
let b = controllers1After[j]
Expand All @@ -2635,7 +2636,7 @@ func TestRuntimeCapabilityControllers(t *testing.T) {

let controllers2After = signer.capabilities.storage.getControllers(forPath: storagePath2)
Test.quickSort(
&controllers2After as &[AnyStruct],
&controllers2After as auth(Mutable) &[AnyStruct],
isLess: fun(i: Int, j: Int): Bool {
let a = controllers2After[i]
let b = controllers2After[j]
Expand Down
8 changes: 4 additions & 4 deletions runtime/resource_duplicate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,9 @@ func TestRuntimeResourceDuplicationUsingDestructorIteration(t *testing.T) {
script := `
access(all) resource Vault {
access(all) var balance: UFix64
access(all) var dictRef: &{Bool: Vault};
access(all) var dictRef: auth(Mutable) &{Bool: Vault};

init(balance: UFix64, _ dictRef: &{Bool: Vault}) {
init(balance: UFix64, _ dictRef: auth(Mutable) &{Bool: Vault}) {
self.balance = balance
self.dictRef = dictRef;
}
Expand All @@ -208,7 +208,7 @@ func TestRuntimeResourceDuplicationUsingDestructorIteration(t *testing.T) {
access(all) fun main(): UFix64 {

let dict: @{Bool: Vault} <- { }
let dictRef = &dict as &{Bool: Vault};
let dictRef = &dict as auth(Mutable) &{Bool: Vault};

var v1 <- create Vault(balance: 1000.0, dictRef); // This will be duplicated
var v2 <- create Vault(balance: 1.0, dictRef); // This will be lost
Expand Down Expand Up @@ -305,7 +305,7 @@ func TestRuntimeResourceDuplicationUsingDestructorIteration(t *testing.T) {
let acc = getAuthAccount(0x1)
acc.save(<-dict, to: /storage/foo)

let ref = acc.borrow<&{Int: R}>(from: /storage/foo)!
let ref = acc.borrow<auth(Mutable) &{Int: R}>(from: /storage/foo)!

ref.forEachKey(fun(i: Int): Bool {
var r4: @R? <- create R()
Expand Down
6 changes: 3 additions & 3 deletions runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7019,7 +7019,7 @@ func TestRuntimeGetCapability(t *testing.T) {
script := []byte(`
access(all) fun main(): Capability {
let dict: {Int: AuthAccount} = {}
let ref = &dict as &{Int: AnyStruct}
let ref = &dict as auth(Mutable) &{Int: AnyStruct}
ref[0] = getAccount(0x01) as AnyStruct
return dict.values[0].getCapability(/private/xxx)
}
Expand Down Expand Up @@ -7054,7 +7054,7 @@ func TestRuntimeGetCapability(t *testing.T) {
script := []byte(`
access(all) fun main(): Capability {
let dict: {Int: AuthAccount} = {}
let ref = &dict as &{Int: AnyStruct}
let ref = &dict as auth(Mutable) &{Int: AnyStruct}
ref[0] = getAccount(0x01) as AnyStruct
return dict.values[0].getCapability(/public/xxx)
}
Expand Down Expand Up @@ -7089,7 +7089,7 @@ func TestRuntimeGetCapability(t *testing.T) {
script := []byte(`
access(all) fun main(): Capability {
let dict: {Int: PublicAccount} = {}
let ref = &dict as &{Int: AnyStruct}
let ref = &dict as auth(Mutable) &{Int: AnyStruct}
ref[0] = getAccount(0x01) as AnyStruct
return dict.values[0].getCapability(/public/xxx)
}
Expand Down
23 changes: 23 additions & 0 deletions runtime/sema/check_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,35 @@ func (checker *Checker) visitIdentifierExpressionAssignment(
return variable.Type
}

var mutableEntitledAccess = NewEntitlementSetAccess(
[]*EntitlementType{MutableEntitlement},
Disjunction,
)

var insertableAndRemovableEntitledAccess = NewEntitlementSetAccess(
[]*EntitlementType{InsertableEntitlement, RemovableEntitlement},
Conjunction,
)

func (checker *Checker) visitIndexExpressionAssignment(
indexExpression *ast.IndexExpression,
) (elementType Type) {

elementType = checker.visitIndexExpression(indexExpression, true)

indexExprTypes := checker.Elaboration.IndexExpressionTypes(indexExpression)
indexedRefType, isReference := referenceType(indexExprTypes.IndexedType)

if isReference &&
!mutableEntitledAccess.PermitsAccess(indexedRefType.Authorization) &&
!insertableAndRemovableEntitledAccess.PermitsAccess(indexedRefType.Authorization) {
checker.report(&UnauthorizedReferenceAssignmentError{
RequiredAccess: [2]Access{mutableEntitledAccess, insertableAndRemovableEntitledAccess},
FoundAccess: indexedRefType.Authorization,
Range: ast.NewRangeFromPositioned(checker.memoryGauge, indexExpression),
})
}

if elementType == nil {
return InvalidType
}
Expand Down
12 changes: 8 additions & 4 deletions runtime/sema/check_member_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,21 @@
}

func shouldReturnReference(parentType, memberType Type) bool {
if memberType == nil || !isReferenceType(parentType) {
if memberType == nil {
return false
SupunS marked this conversation as resolved.
Show resolved Hide resolved
}

Check warning on line 117 in runtime/sema/check_member_expression.go

View check run for this annotation

Codecov / codecov/patch

runtime/sema/check_member_expression.go#L116-L117

Added lines #L116 - L117 were not covered by tests

if _, isReference := referenceType(parentType); !isReference {
return false
}

return memberType.ContainFieldsOrElements()
}

func isReferenceType(typ Type) bool {
func referenceType(typ Type) (*ReferenceType, bool) {
unwrappedType := UnwrapOptionalType(typ)
_, isReference := unwrappedType.(*ReferenceType)
return isReference
refType, isReference := unwrappedType.(*ReferenceType)
return refType, isReference
}

func (checker *Checker) visitMember(expression *ast.MemberExpression) (accessedType Type, resultingType Type, member *Member, isOptional bool) {
Expand Down
6 changes: 5 additions & 1 deletion runtime/sema/check_variable_declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,11 @@ func (checker *Checker) recordReferenceCreation(target, expr ast.Expression) {
}

func (checker *Checker) recordReference(targetVariable *Variable, expr ast.Expression) {
if targetVariable == nil || !isReferenceType(targetVariable.Type) {
if targetVariable == nil {
return
}

if _, isReference := referenceType(targetVariable.Type); !isReference {
return
}

Expand Down
40 changes: 40 additions & 0 deletions runtime/sema/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2929,6 +2929,46 @@ func (e *InvalidAssignmentAccessError) SecondaryError() string {
)
}

// UnauthorizedReferenceAssignmentError

type UnauthorizedReferenceAssignmentError struct {
RequiredAccess [2]Access
turbolent marked this conversation as resolved.
Show resolved Hide resolved
FoundAccess Access
ast.Range
}

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

func (*UnauthorizedReferenceAssignmentError) isSemanticError() {}

func (*UnauthorizedReferenceAssignmentError) IsUserError() {}

func (e *UnauthorizedReferenceAssignmentError) Error() string {
var foundAccess string
if e.FoundAccess == UnauthorizedAccess {
foundAccess = "non-auth"
} else {
foundAccess = fmt.Sprintf("(%s)", e.FoundAccess.Description())
}

return fmt.Sprintf(
"invalid assignment: can only assign to a reference with (%s) or (%s) access, but found a %s reference",
e.RequiredAccess[0].Description(),
e.RequiredAccess[1].Description(),
foundAccess,
)
}

func (e *UnauthorizedReferenceAssignmentError) SecondaryError() string {
return fmt.Sprintf(
"consider taking a reference with `%s` or `%s` access",
e.RequiredAccess[0].Description(),
e.RequiredAccess[1].Description(),
)
}

// InvalidCharacterLiteralError

type InvalidCharacterLiteralError struct {
Expand Down
Loading
Loading