ta0kira / zeolite

Zeolite is a statically-typed, general-purpose programming language.
Apache License 2.0
18 stars 0 forks source link

Edge-case with double parameter substitution in function calls. #73

Closed ta0kira closed 4 years ago

ta0kira commented 4 years ago

The following causes the error:

concrete Type1<#x> {
  @type call<#y> (#x) -> ()
}

define Type1 {
  call (_) {}
}

concrete Type2<#y> {
  @type call () -> ()
}

define Type2 {
  call () {
    // Value<#y> gets subbed in for #x, so call is re-parameterized with an
    // argument of type Value<#y>. When the compiler processes call<Int>, it
    // looks for #y, which it finds; however, it's a different #y.
    \ Type1<Value<#y>>$call<Int>(Value<#y>$create())
  }
}

concrete Value<#z> {
  @type create () -> (Value<#z>)
}

define Value {
  create () {
    return Value<#z>{ }
  }
}

One potential solution is to somehow mark a param as "resolved". For example, add a flag to JustParamName that gets set when substitution happens in uncheckedSubInstance. (Then also get rid of weakGetValueForParam.)

ta0kira commented 4 years ago

Went with the suggested solution above.

ta0kira commented 4 years ago

There's actually still an issue when a function with param #x is inherited in a category that has a top-level param #x and the latter doesn't explicitly override the param.

@value interface Base {
  call<#x> (#x) -> ()
}

concrete Child<#x> {
  refines Base
  // call also has param #x.
}

In this case, Child<Foo> sets the arg type for call to Foo.

It seems like there are two options here:

  1. Make it a compilation error to not override the param name in Child.call. This could be tedious if the function has filters.
  2. Give params an enum that specifies whether it's a category or function params, in place of a "fixed" flag. This might be fine since a sub function is passed recursively rather than the Map itself.
ta0kira commented 4 years ago

As it turns out, the example above fails when validating Child (suggestion 1 above); however, the context of the error is Base.call rather than Child.call.

So, the clash won't actually happen, but the error currently points to the wrong place.

ta0kira commented 4 years ago

Leaving this open and changing the type from "bug" to "enhancement" to implement suggestion 2 above. (Use a category vs. function enum during the separate substitution stages.)

ta0kira commented 4 years ago

Suggestion 2 won't work because the function can't actually be defined if it has a parameter clash. So, the only choice here is to make the error message more clear.