Strange compiler error on ambiguous names
chtenb opened this issue · comments
The problem
Minimal repro:
ref type type1
Field(type2 : type2)
ref struct type2
flag : bool
fun flag(eval : type1)
match eval
Field(type2) -> type2.flag
Error:
repro.kk(9,21): error: types do not match
context : type2.flag
term : type2
inferred type: type2
expected type: type1
Expected behavior
I had expected a different error, something along the lines of error: flag is already defined at (5, 2)
, if the names are ambiguous. If they are not ambiguous I would have expected no error at all.
This is interesting because it desugars to the following:
ref type type1
Field(type2 : type2)
ref struct type2
flag : bool
fun type2/flag(t: type2)
match t
Type2(f) -> f
fun flag(eval : type1)
match eval
Field(type2) -> type2.flag
which since flag
is unqualified and there is there is an unqualified name exactly matching, it complains of a type error, instead of attempting to disambiguate.
If you change your flag
for type1
to:
fun type1/flag(eval : type1)
match eval
Field(type2) -> type2.flag
Then it resolves fine. So it is an issue that mixing qualified names with unqualified names within a module doesn't work well. The error needs to be changed to reflect that, or we need to fall back to disambiguation when encountering a type error in this case. Probably the latter.
Hi @chtenb -- thanks for such early feedback :-) Thanks Tim for the desugaring -- but it is a bit more subtle than that (and I think we should consider the current behavior a bug).
In particular, in the "local scope" any exact match is always taken -- so a local val x = ..
will always be the one considered for an identifier x
. This is to preserve the regular scoping rules of lambda calculus. For implicit parameters we get around this "restriction" by using a locally qualified name for them (i.e. ?eq
== @implicit/eq
and thus a bare identifier eq
is not a direct match and can consider things in the global scope as well.
For your example, the flag
identifier is not a direct match with the global flag
as it's qualified name is currentmodule/flag
-- so why did the compiler not consider type2/flag
as well? Well, that is because the definition is recursive and it has not a full type signature. In such case, the definition cannot be used for polymorphic recursion, and to prevent this the compiler inserts a monomorphic signature in the local scope ... and now the flag
matches exactly while type checking :-( . So if you complete the type of type1/flag
, it will work:
fun flag(eval : type1) : bool
match eval
Field(type2) -> type2.flag
(or if you locally qualify as Tim suggested).
The current behavior is a bug.. but a bit tricky to fix I think as we would need a qualified name in the local scope; I'll look into it.
I think I fixed this issue in the latest dev
-- thanks!