wiz-lang / wiz

A high-level assembly language for writing homebrew software and games on retro console platforms.
http://wiz-lang.org/
Other
409 stars 40 forks source link

Allow assignment to designated storage to implicitly cast when storage matches. #75

Closed Bananattack closed 4 years ago

Bananattack commented 4 years ago

If a register assigning to a variable with designated storage, allow differing types to implicitly cast if the register named matches.

eg. given the following declaration (assuming this declaration is well-formed, where hl is a u16, and sizeof(*u8) == sizeof(u16))

func foo(bar : *u8 in hl) { ... }

Right now, Wiz requires the user to cast hl into a *u8 first, even though the type signature specifies hl as the designated storage:

// ERROR. function expects a *u8 in hl, but got a u16 in hl. (storage matches, but type differs)
foo(hl);
// OK. explicit cast to *u8 in hl. (type and storage match)
foo(hl as *u8);
// ERROR. function expects a *u8 in hl, but got a u16 in de. (type and storage differs)
foo(de);
// ERROR. function expects a *u8 in hl, but got a *u8 in de. (storage differs)
foo(de as *u8);

The proposed change will allow the call to implicitly convert hl into type *u8 in hl, like so:

// OK. Proposed change will allow implicit cast here. (type differs but storage matches)
foo(hl); 
// OK. explicit cast to *u8 in hl. (type and storage match)
foo(hl as *u8);
// still errors:
foo(de);
foo(de as *u8);

Only allow this in cases where the designated storage matches the value being assigned to it.

The examples are shown for function arguments, but same rules apply for variables that use designated storage.

Bananattack commented 4 years ago

I'm having second thoughts about this after doing a bunch of GB code lately. The cast at the call site makes the intent explicit, so you're less likely to miss that you're for example accidentally passing an arbitrary u16 as a pointer to *T. The cast throws away that safety, but it is explicitly noted at the call site, and keeps the type system in the compiler rules clearer by avoiding this form of implicit conversion. Plus if you forward a local variable of equivalent designated storage type to an argument (eg. var ptr : *u8 in hl), doing a call foo(ptr); will pass type-checks without the cast, because it you're passing a definition that already matches the type and storage.

Going to close for now.