onflow / cadence

Cadence, the resource-oriented smart contract programming language 🏃‍♂️
https://developers.flow.com/cadence
Apache License 2.0
531 stars 139 forks source link

Dynamic casting should unwrap optional values #3183

Closed austinkline closed 22 minutes ago

austinkline commented 5 months ago

Why / Impact

Developer convenience

Current Behavior

Dynamic casting (failable casting as?, and force casting as!) an optional value (potentially type-erased to an existential) to a non-optional type currently fails (e.g. failable casting results in nil).

Expected Behavior

I expected (but perhaps shouldn't have) that an optional value being casted into a subtype it conforms to would unwrap it. I'm really not sure if this is a bug or if it's expected though

Steps To Reproduce

I've made a repro contract/transactions to demonstrate

https://github.com/austinkline/cadence-burner-bug

Environment

- Cadence version:Version: v1.15.0-cadence-v1.0.0-preview.12, Commit: c582e63baf4581e4cf021a9b1917c198ca5b6065
- Network: emulator
j1010001 commented 5 months ago

Discord thread: https://discord.com/channels/613813861610684416/621847426201944074/1219773414949126234

turbolent commented 5 months ago

I guess that makes sense. For example, Swift also supports unwrapping using casting:

let hello: String??? = "hello"
let something: Any = hello 
something as! String   // "hello" of type `String`

However, casting to Any does not:

something as! Any   // Optional(Optional(Optional("hello"))) of type `String???`

Then again, the behaviour does not seem to be about existential types, because those are also unwrapped:

protocol I {}

struct S: I {}

let hello: S???? = S()
let something: Any = hello 
something as! any I   // S() of type `S`, not `Optional(Optional(Optional(S())))` of type `S???`