cadence icon indicating copy to clipboard operation
cadence copied to clipboard

Enable iteration over resource arrays

Open turbolent opened this issue 3 years ago • 3 comments

Issue To Be Solved

Currently the for-in loop is only supported for struct arrays, not resource arrays.

Enable for-in loops to iterate over arrays of resources, binding an authorized reference:

pub resource R {
    pub let name: String

    init(name: String) {
        self.name = name
    }
}

let rs <- [
    <-create R(name: "bar"), 
    <-create R(name: "foo")
]

for ref in rs {
	// ref has type `&R`
    log(ref.name)
}

for ref: auth &R in rs {
	// ref has type `auth &R`
    log(ref.name)
}

destroy rs

Definition of Done

  • Parser:
    • [ ] Allow optional type annotation for value variable of for-loop
  • Checker:
    • [ ] Check optional type annotation for value variable of for-loop
    • [ ] Bind references when looping over resource arrays. Use optional type annotation to determine if authorized or not. Default to unauthorized to prevent security issues. Store type in elaboration
  • Interpreter:
    • [ ] Bind reference when looping over resource arrays. Use type of elaboration
  • [ ] Tests
  • [ ] Documentation

turbolent avatar Mar 17 '21 20:03 turbolent

Hey @turbolent I'd also be interested in working on this one as well with some guidance (since the other issue was pretty fun to tackle). Any other code snippets I could look at for reference?

ceelo777 avatar Mar 30 '21 18:03 ceelo777

@ceelo777 Nice! 🎉

Here are some first pointers:

  • The checker currently forbids iterating over resources of any kind:

    https://github.com/onflow/cadence/blob/3bbc8c8a1a85f729a6a7ae1537769f6cb8de4964/runtime/sema/check_for.go#L43-L48

    This needs to be removed.

  • Now that the value that is iterated over may be a resource, checkUnusedExpressionResourceLoss must be called:

    https://github.com/onflow/cadence/blob/3bbc8c8a1a85f729a6a7ae1537769f6cb8de4964/runtime/sema/checker.go#L1607-L1624

  • The checker declares the iteration variable as the element type:

    https://github.com/onflow/cadence/blob/3bbc8c8a1a85f729a6a7ae1537769f6cb8de4964/runtime/sema/check_for.go#L66-L66

    This needs to be changed to an authorized reference of the element type, if the iterated over value is an array of resources.

  • The interpreter sets the iteration value to each element of the array:

https://github.com/onflow/cadence/blob/3bbc8c8a1a85f729a6a7ae1537769f6cb8de4964/runtime/interpreter/interpreter_statement.go#L240-L240

This needs to be changed to an authorized ephemeral reference to the element.

turbolent avatar Mar 30 '21 23:03 turbolent

Iterating over a resource array is already possible, but requires some boilerplate code (Playground: https://play.onflow.org/58c1529b-7df0-4a8c-8cbe-3c78d72514b2?type=script&id=9bb82938-eedf-4ae6-b914-f266d2837016&storage=none):

pub contract Test {

    pub resource R {}

    pub fun createR(): @R {
        return <-create Test.R()
    }
}
import Test from 0x1

pub fun main() {
    let rs <- [
        <- Test.createR(),
        <- Test.createR(),
        <- Test.createR()
    ]


    var i = 0
    while i < rs.length {
       let ref = &rs[i] as &Test.R
     
       log(ref.uuid)

       i = i + 1
    }

    destroy rs
}

turbolent avatar Mar 28 '22 16:03 turbolent