From f59cad4e99851d89f07cd6e48e810063f77dd4e4 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 27 Jul 2023 12:35:34 -0700 Subject: [PATCH 1/6] Add section on new member access semantics for references --- docs/cadence/language/access-control.md | 4 +- docs/cadence/language/references.mdx | 139 +++++++++++++++++------- src/data/pathMapping/cadence.yml | 2 +- 3 files changed, 104 insertions(+), 41 deletions(-) diff --git a/docs/cadence/language/access-control.md b/docs/cadence/language/access-control.md index 3dea6efd03..4216079e71 100644 --- a/docs/cadence/language/access-control.md +++ b/docs/cadence/language/access-control.md @@ -341,9 +341,9 @@ resource OuterResource { With this pattern, we can store a `SubResource` on an `OuterResource` value, and create different ways to access that nested resource depending on the entitlement one posseses. -Somoneone with only an unauthorized reference to `OuterResource` can only call the `getPubRef` function, +Someone with only an unauthorized reference to `OuterResource` can only call the `getPubRef` function, and thus can only get an unauthorized reference to `SubResource` that lets them call `foo`. -However, someone with a `OuterEntitlement`-authorized refererence to the `OuterResource` can call the `getEntitledRef` function, +However, someone with a `OuterEntitlement`-authorized reference to the `OuterResource` can call the `getEntitledRef` function, giving them a `SubEntitlement`-authorized reference to `SubResource` that allows them to call `bar`. This pattern is functional, but it is unfortunate that we are forced to "duplicate" the accessors to `SubResource`, diff --git a/docs/cadence/language/references.mdx b/docs/cadence/language/references.mdx index d564980d28..ba0ba64ce9 100644 --- a/docs/cadence/language/references.mdx +++ b/docs/cadence/language/references.mdx @@ -99,8 +99,8 @@ counterRef.increment() counterRef.count // is `43` ``` -References can be freely upcasted and downcasted, and are covariant in their referenced type. -So, for example, for some struct `S`, `&S` is a subtype of `&AnyStruct`, but not of `&Int`. +References can be freely upcasted and downcasted, and are covariant in their referenced type. +So, for example, for some struct `S`, `&S` is a subtype of `&AnyStruct`, but not of `&Int`. ```cadence // Create an reference to the counter, @@ -117,7 +117,7 @@ countRef.count // is `43` countRef.increment() // We can conditionally downcast `countRef` to a `Counter` if it has -// that type at runtime. +// that type at runtime. // let counterRef2: &Counter = countRef as? &Counter counterRef2.increment() @@ -128,18 +128,18 @@ Instead, consider [storing a capability and borrowing it](capability-based-acces ## Authorized References -By default, references are **unauthorized**. +By default, references are **unauthorized**. However, they may also be **authorized** to a set of [entitlements](./access-control.md#entitlements) -Authorized references have the `auth` modifier, +Authorized references have the `auth` modifier, along with the set of entitlements to which they are authorized. The full syntax is: `auth(E, F, G) &T` for a reference authorized to `E`, `F` and `G`, -or `auth(E | F | G) &T` for a refernece authorized to `E`, `F`, **or** `G`. +or `auth(E | F | G) &T` for a refernece authorized to `E`, `F`, **or** `G`. Authorized references are subtypes of unauthorized references. -Entitlements can only be given to references when they are created, -and references to a value can only be created by the owner of the value. -When creating a reference, that reference can be given any set of entitlements the value owner wishes to add. +Entitlements can only be given to references when they are created, +and references to a value can only be created by the owner of the value. +When creating a reference, that reference can be given any set of entitlements the value owner wishes to add. Possessing an entitlement allows a reference to have access to functions and fields on its referenced type that require that entitlement. E.g, if we extended the `HasCount` interface with a function: @@ -149,11 +149,11 @@ entitlement Reset resource interface HasCount { count: Int - access(Reset) fun resetCount() + access(Reset) fun resetCount() } ``` -Then an unauthorized reference of type `&{HasCount}` would be unable to call `resetCount`. +Then an unauthorized reference of type `&{HasCount}` would be unable to call `resetCount`. However, we can create a reference that can, like so: @@ -164,44 +164,44 @@ let authCountRef: auth(Reset) &{HasCount} = &counter authCountRef.resetCount() ``` -It is important to note that while references are covariant (and downcastable) with respect to their reference type, -the authorization portion of the reference can never be downcast. -In fact, the only way to "add" entitlements to a reference is to do so at the time of its creation, like in the example above. -A reference will never have any more entitlements than the set with which it was created, -and the set of entitlements on a reference at runtime will always match the set expressed in its static type. +It is important to note that while references are covariant (and downcastable) with respect to their reference type, +the authorization portion of the reference can never be downcast. +In fact, the only way to "add" entitlements to a reference is to do so at the time of its creation, like in the example above. +A reference will never have any more entitlements than the set with which it was created, +and the set of entitlements on a reference at runtime will always match the set expressed in its static type. One implication of this is that upcasting an authorized reference actually changes its runtime type: ``` let authCountRef: auth(Reset) &{HasCount} = &counter let unauthCountRef = authCountRef as &{HasCount} -let authCountRef2 = unauthCountRef as? auth(Reset) &{HasCount} +let authCountRef2 = unauthCountRef as? auth(Reset) &{HasCount} -// Invalid: `authCountRef2` is `nil`, as the upcast of `authCountRef` cleared the -// `Reset` entitlement from the reference, meaning that it cannot be regained on downcasting. +// Invalid: `authCountRef2` is `nil`, as the upcast of `authCountRef` cleared the +// `Reset` entitlement from the reference, meaning that it cannot be regained on downcasting. authCountRef2.resetCount() ``` -The benefit of this is that there is never any "surprising" behavior with regards to entitlements, -every reference value is transparent about what it is capable of at runtime. +The benefit of this is that there is never any "surprising" behavior with regards to entitlements, +every reference value is transparent about what it is capable of at runtime. While entitlement sets on references cannot be downcast, they can be upcast, or used in places expecting supertypes, -and have special subtyping rules based on whether they are `|` or `,`-separated sets. +and have special subtyping rules based on whether they are `|` or `,`-separated sets. -In general, an entitlement set `{Us}` is a subtype of an entitlement set `{Vs}` when `{Us}` has more entitlements -in it than `{Vs}`, and when both are `,`-separated (as they will be in most cases), this is the rule exactly: +In general, an entitlement set `{Us}` is a subtype of an entitlement set `{Vs}` when `{Us}` has more entitlements +in it than `{Vs}`, and when both are `,`-separated (as they will be in most cases), this is the rule exactly: `{Us}` is a subset of `{Vs}` when it is a superset of `{Vs}`. Conversely, if both are `|`-separated, the rule is reversed: -`{Us}` is a subset of `{Vs}` when it is a subset of `{Vs}`. -It may be helpful to think of this as saying that `{Us}` is more specific than `{Vs}` in this case; -`{Vs}` expresses a set of entitlements that the reference **might** possess, +`{Us}` is a subset of `{Vs}` when it is a subset of `{Vs}`. +It may be helpful to think of this as saying that `{Us}` is more specific than `{Vs}` in this case; +`{Vs}` expresses a set of entitlements that the reference **might** possess, while `{Us}` is expressing a more specific set of potential entitlements. -Lastly, if `{Us}` is `,`-separated while `{Vs}` is `|`-separated, -then `{Us}` is a subset of `{Vs}` when any of the `Us` also appears in `{Vs}`. -To see why, consider again that `{Vs}` expresses a set of entitlements that the reference **might** possess, -and as long as at least one of these entitlements is in `{Us}` (which is a set of entitlements that we **know** the reference has), -then the description provided by `{Vs}` is correct. +Lastly, if `{Us}` is `,`-separated while `{Vs}` is `|`-separated, +then `{Us}` is a subset of `{Vs}` when any of the `Us` also appears in `{Vs}`. +To see why, consider again that `{Vs}` expresses a set of entitlements that the reference **might** possess, +and as long as at least one of these entitlements is in `{Us}` (which is a set of entitlements that we **know** the reference has), +then the description provided by `{Vs}` is correct. As an example to illustrate these rules: @@ -235,7 +235,7 @@ eOrFRef as auth(E, F) &T ### References and Entitlement Mappings In most situations, an [entitlement mapping](./access-control.md#entitlement-mappings) is valid in the `auth` portion of a reference type. -However, in certain specific circumstances in the definition of a field or function on a composite type, an entitlement mapping may be used in an `auth` modifier. +However, in certain specific circumstances in the definition of a field or function on a composite type, an entitlement mapping may be used in an `auth` modifier. When a field is defined with an entitlement mapping: @@ -250,7 +250,7 @@ resource interface I { Here, the `M` in `auth(M) &T` indicates that the entitlements that the reference produced by an `iRef.foo` access will have are determined by the entitlements to `I` that `iRef` has, for some `iRef` value that is a reference to `{I}`. Conceptually, -it creates a correspondence between the "output" reference's type and the "input" access modifier. +it creates a correspondence between the "output" reference's type and the "input" access modifier. When an accessor function is defined with an entitlement mapping: @@ -268,11 +268,74 @@ resource I { ``` The `M` in the `auth(M) &T` of the function's return type annotation indicates the same thing as in the field case. -However, in this example `M` is also used in a reference type within the body of the function. -Inside the body of function with entitlement-mapped access, -the name of the entitlement mapping may be used as a stand-in for the output entitlements of the map. +However, in this example `M` is also used in a reference type within the body of the function. +Inside the body of function with entitlement-mapped access, +the name of the entitlement mapping may be used as a stand-in for the output entitlements of the map. -## Reference validity +## Field and Index Access + +References to container types (structs/resources, dictionaries and arrays) can be used to access (read/write) fields +or elements of the container. + +When a field/index is read through a reference, it wil return: +- A reference, if the field / value at index is also container-typed. +- Or, the concrete value, if the value is a primitive type. + +For example, consider the below `Collection` resource which has two fields: one (id) is String-typed, +and the other (ownedNFTs) is dictionary-typed. + +```cadence +resource Collection { + + // Primitive-typed field + access(all) var id: String + + // Dictionary typed field + access(all) var ownedNFTs: @{UInt64: NFT} +} +``` + +Thus, + +```cadence +var collectionRef: &Collection = ... + +// `collectionRef.ownedNFTs` would return a reference of type `&{UInt64: NFT}`. +var ownedNFTsRef: &{UInt64: NFT} = collectionRef.ownedNFTs + +// Whereas, `collectionRef.id` would return the value, since it is a primitive type. +var id: String = collectionRef.id +``` + +Similarly, accessing an element of an array/dictionary will return a reference. + +```cadence +// Index-access to an array reference would return a reference to the element. +var resourceArrayRef: &[AnyResource] = ... +var elementRef: &AnyResource = collectionArrayRef[2] + +// Whereas, if the array is of primitive type, it will return the concrete value. +var intArrayRef: &[Int] = ... +var element: Int = intArrayRef[2] +``` + +```cadence +// Index-access to a dictionary reference would return a refe rence to the value. +var resourceDictionaryRef: &{String: AnyResource} = ... +var valueRef: &AnyResource? = resourceDictionaryRef["two"] + +// Whereas, if the dictionary values are of primitive type, it will return the concrete value. +var intDictionaryRef: &{String: Int} = ... +var value: Int? = intDictionaryRef["two"] +``` + +It is also important to note that, in the above examples, the returned references have no entitlements. +i.e: they are non-auth references. + +To get entitled references for struct/resource fields, they must be defined with [entitlement mappings.](./access-control.md#entitlement-mappings). +However, accessing a value at an index/key of an array/dictionary reference would always return a non-auth reference. + +## Reference Validity Ephemeral references stay valid throughout the course of the program. However, **references to resources** can become invalid during the execution of a program, diff --git a/src/data/pathMapping/cadence.yml b/src/data/pathMapping/cadence.yml index 640c43f7ca..02f8d69912 100644 --- a/src/data/pathMapping/cadence.yml +++ b/src/data/pathMapping/cadence.yml @@ -24,7 +24,7 @@ ./docs/language/index.md: docs/cadence/language/index.md ./docs/language/interfaces.mdx: docs/cadence/language/interfaces.mdx ./docs/language/operators.md: docs/cadence/language/operators.md -./docs/language/references.md: docs/cadence/language/references.md +./docs/language/references.mdx: docs/cadence/language/references.mdx ./docs/language/resources.mdx: docs/cadence/language/resources.mdx ./docs/language/restricted-types.md: docs/cadence/language/restricted-types.md ./docs/language/run-time-types.md: docs/cadence/language/run-time-types.md From c68da4d0177fa45a25fa5708a46f085c9976d8c9 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 27 Jul 2023 13:08:08 -0700 Subject: [PATCH 2/6] Update with built-in entitlements --- docs/cadence/language/access-control.md | 19 ++++++++++++- docs/cadence/language/references.mdx | 31 ++++++++++++++++++++- docs/cadence/language/values-and-types.mdx | 32 +++++++++++----------- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/docs/cadence/language/access-control.md b/docs/cadence/language/access-control.md index 4216079e71..17d86f8325 100644 --- a/docs/cadence/language/access-control.md +++ b/docs/cadence/language/access-control.md @@ -414,4 +414,21 @@ entitlement mapping M { } ``` -attempting to map `(A | D)` through `M` will fail, since `A` should map to `(B, C)` and `D` should map to `E`, but these two outputs cannot be combined into a disjunctive set. \ No newline at end of file +attempting to map `(A | D)` through `M` will fail, since `A` should map to `(B, C)` and `D` should map to `E`, but these two outputs cannot be combined into a disjunctive set. + +### Built-in Mutability Entitlements + +A prominent use-case of entitlements is to control access to object based on mutability. +For example, in a struct/resource/contract, the author would want to control the access to certain fields to be read-only, +and while some fields to be mutable, etc. + +In order to support this, Cadence hase built-in set of entitlements that can be used to access control base on mutability. +- `Insert` +- `Remove` +- `Mutate` + +These are primarily used by built-in array and dictionary functions, but are also usable by any user to control access +in their own composite type definitions. + +While there is no such a thing as entitlement composition/inheritance, the `Mutate` entitlement is intended to be used +as an equivalent form to the conjunction of `{Insert, Remove}` entitlements. diff --git a/docs/cadence/language/references.mdx b/docs/cadence/language/references.mdx index ba0ba64ce9..cb5e561271 100644 --- a/docs/cadence/language/references.mdx +++ b/docs/cadence/language/references.mdx @@ -332,9 +332,38 @@ var value: Int? = intDictionaryRef["two"] It is also important to note that, in the above examples, the returned references have no entitlements. i.e: they are non-auth references. -To get entitled references for struct/resource fields, they must be defined with [entitlement mappings.](./access-control.md#entitlement-mappings). +To get entitled references for struct/resource fields, they must be defined with [entitlement mappings](./access-control.md#entitlement-mappings). However, accessing a value at an index/key of an array/dictionary reference would always return a non-auth reference. +### Index Assignment + +Assigning to an index of an array or a dictionary reference is an entitled-operation. +In other words, the assignment operator for arrays/dictionaries would also have the `Mutate` and `Insert` +[built-in entitlements](./access-control.md#built-in-mutability-entitlements). + +Think of assignment as a built-in function with `Mutate` and `Insert` entitlements. e.g: + +```cadence +access(Mutate | Insert) set(keyOrIndex, value) { ... } +``` + +Thus, + +```cadence +var arrayRef = &array as &[String] +arrayRef[2] = "John" // Static Error: updating via a read-only reference + +var mutableArrayRef = &array as auth(Mutate) &[String] +mutableArrayRef[2] = "John" // OK + +var insertableArrayRef = &array as auth(Insert) &[String] +insertableArrayRef[2] = "John" // OK + +var removableArrayRef = &array as auth(Remove) &[String] +removableArrayRef[2] = "John" // Static Error: doesn't have the required entitlement +``` + + ## Reference Validity Ephemeral references stay valid throughout the course of the program. diff --git a/docs/cadence/language/values-and-types.mdx b/docs/cadence/language/values-and-types.mdx index e3a4e7e22d..269a456949 100644 --- a/docs/cadence/language/values-and-types.mdx +++ b/docs/cadence/language/values-and-types.mdx @@ -1116,7 +1116,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - fun concat(_ array: T): T + access(all) fun concat(_ array: T): T ``` Concatenates the parameter `array` to the end @@ -1145,7 +1145,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - fun contains(_ element: T): Bool + access(all) fun contains(_ element: T): Bool ``` Returns true if the given element of type `T` is in the array. @@ -1170,7 +1170,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - fun firstIndex(of: T): Int? + access(all) fun firstIndex(of: T): Int? ``` Returns the index of the first element matching the given object in the array, nil if no match. @@ -1191,7 +1191,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - fun slice(from: Int, upTo: Int): [T] + access(all) fun slice(from: Int, upTo: Int): [T] ``` Returns an array slice of the elements @@ -1218,7 +1218,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - fun reverse(): [T] + access(all) fun reverse(): [T] ``` Returns a new array with contents in the reversed order. @@ -1233,7 +1233,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. ``` ```cadence - fun reverse(): [T; N] + access(all) fun reverse(): [T; N] ``` Returns a new fixed-sized array of same size with contents in the reversed order. @@ -1253,7 +1253,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - fun append(_ element: T): Void + access(Mutate | Insert) fun append(_ element: T): Void ``` Adds the new element `element` of type `T` to the end of the array. @@ -1276,7 +1276,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - fun appendAll(_ array: T): Void + access(Mutate | Insert) fun appendAll(_ array: T): Void ``` Adds all the elements from `array` to the end of the array @@ -1300,7 +1300,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - fun insert(at: Int, _ element: T): Void + access(Mutate | Insert) fun insert(at: Int, _ element: T): Void ``` Inserts the new element `element` of type `T` @@ -1331,7 +1331,7 @@ It is invalid to use one of these functions on a fixed-sized array. ``` - ```cadence - fun remove(at: Int): T + access(Mutate | Remove) fun remove(at: Int): T ``` Removes the element at the given `index` from the array and returns it. @@ -1356,7 +1356,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - fun removeFirst(): T + access(Mutate | Remove) fun removeFirst(): T ``` Removes the first element from the array and returns it. @@ -1386,7 +1386,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - fun removeLast(): T + access(Mutate | Remove) fun removeLast(): T ``` Removes the last element from the array and returns it. @@ -1568,7 +1568,7 @@ booleans[0] = true - ```cadence - fun insert(key: K, _ value: V): V? + access(Mutate | Insert) fun insert(key: K, _ value: V): V? ``` Inserts the given value of type `V` into the dictionary under the given `key` of type `K`. @@ -1634,7 +1634,7 @@ booleans[0] = true - ```cadence - let keys: [K] + access(Mutate | Remove) let keys: [K] ``` Returns an array of the keys of type `K` in the dictionary. This does not @@ -1674,7 +1674,7 @@ booleans[0] = true - ```cadence - fun containsKey(key: K): Bool + access(all) fun containsKey(key: K): Bool ``` Returns true if the given key of type `K` is in the dictionary. @@ -1699,7 +1699,7 @@ booleans[0] = true - ```cadence - fun forEachKey(_ function: fun(K): Bool): Void + access(all) fun forEachKey(_ function: fun(K): Bool): Void ``` Iterate through all the keys in the dictionary, exiting early if the passed function returns false. From 0f95aa4ad11ca85a05e0a20a7829f3a122541f2b Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 27 Jul 2023 13:15:45 -0700 Subject: [PATCH 3/6] Update with entitlement mappings improvements --- docs/cadence/language/access-control.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/docs/cadence/language/access-control.md b/docs/cadence/language/access-control.md index 17d86f8325..2da0d8da9a 100644 --- a/docs/cadence/language/access-control.md +++ b/docs/cadence/language/access-control.md @@ -369,13 +369,7 @@ resource SubResource { } resource OuterResource { - access(self) let childResource: @SubResource - - // by referering to `Map` here, we declare that the entitlements we receive when accessing the `getRef` function on this resource - // will depend on the entitlements we possess to the resource during the access. - access(Map) fun getRef(): auth(Map) &SubResource { - return &self.childResource as auth(Map) &SubResource - } + access(Map) let childResource: @SubResource init(r: @SubResource) { self.childResource = r @@ -384,19 +378,17 @@ resource OuterResource { // given some value `r` of type `@OuterResource` let pubRef = &r as &OuterResource -let pubSubRef = pubRef.getRef() // has type `&SubResource` +let pubSubRef = pubRef.childResource // has type `&SubResource` let entitledRef = &r as auth(OuterEntitlement) &OuterResource -let entitledSubRef = entitledRef.getRef() // `OuterEntitlement` is defined to map to `SubEntitlement`, so this access yields a value of type `auth(SubEntitlement) &SubResource` +let entitledSubRef = entitledRef.childResource // `OuterEntitlement` is defined to map to `SubEntitlement`, so this access yields a value of type `auth(SubEntitlement) &SubResource` Entitlement // `r` is an owned value, and is thus considered "fully-entitled" to `OuterResource`, // so this access yields a value authorized to the entire image of `Map`, in this case `SubEntitlement` -let alsoEntitledSubRef = r.getRef() +let alsoEntitledSubRef = r.childResource ``` -{/* TODO: Update once mappings can be used on regular composite fields */} - Entitlement mappings may be used either in accessor functions (as in the example above), or in fields whose types are references. Note that this latter usage will necessarily make the type of the composite non-storage, since it will have a reference field. From c9dc5561a4fce72983731da95ff55534dab9c13d Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 31 Jul 2023 08:16:18 -0700 Subject: [PATCH 4/6] Apply suggestions from code review Co-authored-by: Daniel Sainati --- docs/cadence/language/access-control.md | 2 +- docs/cadence/language/references.mdx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/cadence/language/access-control.md b/docs/cadence/language/access-control.md index 2da0d8da9a..22f0ea794e 100644 --- a/docs/cadence/language/access-control.md +++ b/docs/cadence/language/access-control.md @@ -422,5 +422,5 @@ In order to support this, Cadence hase built-in set of entitlements that can be These are primarily used by built-in array and dictionary functions, but are also usable by any user to control access in their own composite type definitions. -While there is no such a thing as entitlement composition/inheritance, the `Mutate` entitlement is intended to be used +While Cadence does not support entitlement composition or inheritance, the `Mutate` entitlement is intended to be used as an equivalent form to the conjunction of `{Insert, Remove}` entitlements. diff --git a/docs/cadence/language/references.mdx b/docs/cadence/language/references.mdx index cb5e561271..322a79582d 100644 --- a/docs/cadence/language/references.mdx +++ b/docs/cadence/language/references.mdx @@ -277,7 +277,7 @@ the name of the entitlement mapping may be used as a stand-in for the output ent References to container types (structs/resources, dictionaries and arrays) can be used to access (read/write) fields or elements of the container. -When a field/index is read through a reference, it wil return: +When a field/index is read through a reference, it will return: - A reference, if the field / value at index is also container-typed. - Or, the concrete value, if the value is a primitive type. @@ -314,17 +314,17 @@ Similarly, accessing an element of an array/dictionary will return a reference. var resourceArrayRef: &[AnyResource] = ... var elementRef: &AnyResource = collectionArrayRef[2] -// Whereas, if the array is of primitive type, it will return the concrete value. +// Whereas, if the array is of a primitive type, it will return the concrete value. var intArrayRef: &[Int] = ... var element: Int = intArrayRef[2] ``` ```cadence -// Index-access to a dictionary reference would return a refe rence to the value. +// Index-access to a dictionary reference would return a reference to the value. var resourceDictionaryRef: &{String: AnyResource} = ... var valueRef: &AnyResource? = resourceDictionaryRef["two"] -// Whereas, if the dictionary values are of primitive type, it will return the concrete value. +// Whereas, if the dictionary values are of a primitive type, it will return the concrete value. var intDictionaryRef: &{String: Int} = ... var value: Int? = intDictionaryRef["two"] ``` From 8d8fd5c116e1d50f68e45f4a5b9c03543103c46e Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 1 Aug 2023 14:34:17 -0700 Subject: [PATCH 5/6] Refactor --- docs/cadence/language/access-control.md | 10 +++-- docs/cadence/language/references.mdx | 10 +++-- docs/cadence/language/values-and-types.mdx | 48 ++++++++++++++-------- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/docs/cadence/language/access-control.md b/docs/cadence/language/access-control.md index 22f0ea794e..3ece17bc72 100644 --- a/docs/cadence/language/access-control.md +++ b/docs/cadence/language/access-control.md @@ -350,7 +350,8 @@ This pattern is functional, but it is unfortunate that we are forced to "duplica duplicating the code and storing two functions on the object, essentially creating two different views to the same object that are stored as different functions. To avoid necessitating this duplication, we add support to the language for "entitlement mappings", -a way to declare statically how entitlements are propagated from parents to child objects in a nesting hierarchy. +a way to declare statically how entitlements are propagated from parents to child objects in a nesting hierarchy. + So, the above example could be equivalently written as: ```cadence @@ -369,6 +370,8 @@ resource SubResource { } resource OuterResource { + // by referering to `Map` here, we declare that the entitlements we receive when accessing the `childResource` field + // on this resource will depend on the entitlements we possess to the resource during the access. access(Map) let childResource: @SubResource init(r: @SubResource) { @@ -389,8 +392,9 @@ Entitlement let alsoEntitledSubRef = r.childResource ``` -Entitlement mappings may be used either in accessor functions (as in the example above), or in fields whose types are references. Note that this latter -usage will necessarily make the type of the composite non-storage, since it will have a reference field. +Entitlement mappings may be used either in accessor functions (as in the example above), or in fields whose types are +either references, or containers (structs/resources, dictionaries and arrays). +Note that having a reference field will necessarily make the type of the composite non-storage. {/* TODO: once the Account type refactor is complete and the documentation updated, let's link here to the Account type page as an example of more complex entitlement mappings */} Entitlement mappings need not be 1:1; it is valid to define a mapping where multiple inputs map to the same output, or where one input maps to multiple outputs. diff --git a/docs/cadence/language/references.mdx b/docs/cadence/language/references.mdx index 322a79582d..04d9517fa6 100644 --- a/docs/cadence/language/references.mdx +++ b/docs/cadence/language/references.mdx @@ -341,10 +341,10 @@ Assigning to an index of an array or a dictionary reference is an entitled-opera In other words, the assignment operator for arrays/dictionaries would also have the `Mutate` and `Insert` [built-in entitlements](./access-control.md#built-in-mutability-entitlements). -Think of assignment as a built-in function with `Mutate` and `Insert` entitlements. e.g: +Think of assignment as a built-in function with `Mutate` or `(Insert, Remove)` entitlements. e.g: ```cadence -access(Mutate | Insert) set(keyOrIndex, value) { ... } +access(Mutate | (Insert, Remove)) set(keyOrIndex, value) { ... } ``` Thus, @@ -357,12 +357,14 @@ var mutableArrayRef = &array as auth(Mutate) &[String] mutableArrayRef[2] = "John" // OK var insertableArrayRef = &array as auth(Insert) &[String] -insertableArrayRef[2] = "John" // OK +insertableArrayRef[2] = "John" // Static Error: doesn't have the required entitlement var removableArrayRef = &array as auth(Remove) &[String] removableArrayRef[2] = "John" // Static Error: doesn't have the required entitlement -``` +var insertableAndRemovableArrayRef = &array as auth(Insert, Remove) &[String] +insertableAndRemovableArrayRef[2] = "John" // OK +``` ## Reference Validity diff --git a/docs/cadence/language/values-and-types.mdx b/docs/cadence/language/values-and-types.mdx index 269a456949..c924fd1a66 100644 --- a/docs/cadence/language/values-and-types.mdx +++ b/docs/cadence/language/values-and-types.mdx @@ -1116,7 +1116,8 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - access(all) fun concat(_ array: T): T + access(all) + fun concat(_ array: T): T ``` Concatenates the parameter `array` to the end @@ -1145,7 +1146,8 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - access(all) fun contains(_ element: T): Bool + access(all) + fun contains(_ element: T): Bool ``` Returns true if the given element of type `T` is in the array. @@ -1170,7 +1172,8 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - access(all) fun firstIndex(of: T): Int? + access(all) + fun firstIndex(of: T): Int? ``` Returns the index of the first element matching the given object in the array, nil if no match. @@ -1191,7 +1194,8 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - access(all) fun slice(from: Int, upTo: Int): [T] + access(all) + fun slice(from: Int, upTo: Int): [T] ``` Returns an array slice of the elements @@ -1218,7 +1222,8 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - access(all) fun reverse(): [T] + access(all) + fun reverse(): [T] ``` Returns a new array with contents in the reversed order. @@ -1233,7 +1238,8 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. ``` ```cadence - access(all) fun reverse(): [T; N] + access(all) + fun reverse(): [T; N] ``` Returns a new fixed-sized array of same size with contents in the reversed order. @@ -1253,7 +1259,8 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Insert) fun append(_ element: T): Void + access(Mutate | Insert) + fun append(_ element: T): Void ``` Adds the new element `element` of type `T` to the end of the array. @@ -1276,7 +1283,8 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Insert) fun appendAll(_ array: T): Void + access(Mutate | Insert) + fun appendAll(_ array: T): Void ``` Adds all the elements from `array` to the end of the array @@ -1300,7 +1308,8 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Insert) fun insert(at: Int, _ element: T): Void + access(Mutate | Insert) + fun insert(at: Int, _ element: T): Void ``` Inserts the new element `element` of type `T` @@ -1331,7 +1340,8 @@ It is invalid to use one of these functions on a fixed-sized array. ``` - ```cadence - access(Mutate | Remove) fun remove(at: Int): T + access(Mutate | Remove) + fun remove(at: Int): T ``` Removes the element at the given `index` from the array and returns it. @@ -1356,7 +1366,8 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Remove) fun removeFirst(): T + access(Mutate | Remove) + fun removeFirst(): T ``` Removes the first element from the array and returns it. @@ -1386,7 +1397,8 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Remove) fun removeLast(): T + access(Mutate | Remove) + fun removeLast(): T ``` Removes the last element from the array and returns it. @@ -1568,7 +1580,8 @@ booleans[0] = true - ```cadence - access(Mutate | Insert) fun insert(key: K, _ value: V): V? + access(Mutate | Insert) + fun insert(key: K, _ value: V): V? ``` Inserts the given value of type `V` into the dictionary under the given `key` of type `K`. @@ -1634,7 +1647,8 @@ booleans[0] = true - ```cadence - access(Mutate | Remove) let keys: [K] + access(Mutate | Remove) + let keys: [K] ``` Returns an array of the keys of type `K` in the dictionary. This does not @@ -1674,7 +1688,8 @@ booleans[0] = true - ```cadence - access(all) fun containsKey(key: K): Bool + access(all) + fun containsKey(key: K): Bool ``` Returns true if the given key of type `K` is in the dictionary. @@ -1699,7 +1714,8 @@ booleans[0] = true - ```cadence - access(all) fun forEachKey(_ function: fun(K): Bool): Void + access(all) + fun forEachKey(_ function: fun(K): Bool): Void ``` Iterate through all the keys in the dictionary, exiting early if the passed function returns false. From 6dbc4fbe42183fab8df8287eb268c0708562bae0 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 2 Aug 2023 09:23:09 -0700 Subject: [PATCH 6/6] Add a note on nested entitlements syntax --- docs/cadence/language/references.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/cadence/language/references.mdx b/docs/cadence/language/references.mdx index 04d9517fa6..a3c20f8a28 100644 --- a/docs/cadence/language/references.mdx +++ b/docs/cadence/language/references.mdx @@ -347,6 +347,9 @@ Think of assignment as a built-in function with `Mutate` or `(Insert, Remove)` e access(Mutate | (Insert, Remove)) set(keyOrIndex, value) { ... } ``` +Note that the syntax for having nested entitlements in access modifiers like `(Mutate | (Insert, Remove))` +is not currently supported, but this is for illustration purpose only. + Thus, ```cadence