Draft: Reject puns in T2T (#24153)
This patch contains pun-related changes extracted from !11166 (closed).
I'm opening this MR (marked as Draft) so that the changes don't get lost, but I haven't properly addressed Simon's comments, cited below
Simon: !11166 (comment 533054)
I don't think we need this complexity. Not only is there a new data type, a new field in every HsVar, but even a new type class -- sigh!!
Fortunately I don't think need all this.
During typechecking we have
tcl_rdr :: LocalRdrEnv
, which gives the renamer env. We can easily check for punning here, rather than doing so in the renamer.Not only is that simpler, but it's also more efficient: only happens in T2T rather than all the time.
Vlad: !11166 (comment 533098)
Right, this is how I attempted to implement it initially. Unfortunately, I no longer think it's possible. We need an
RdrName
, not aName
, because we want to distinguish qualified and unqualified occurrences. Compare
f (Just @a a) = vfun a -- pun in T2T
f (Just @a a) = vfun OtherMod.a -- no pun in T2T
In the first case, we want to report a pun because it's unclear which
a
is referred to byvfun a
. In the second case, there's no pun, asOtherMod.a
refers to something else entirely (somea
coming from another module)We can easily check this in the renamer, where we have an
RdrName
, by usingpromoteRdrName
and looking up the promoted name in the environmentpromoteRdrName :: RdrName -> Maybe RdrName promoteRdrName (Unqual occ) = fmap Unqual (promoteOccName occ) promoteRdrName (Qual m occ) = fmap (Qual m) (promoteOccName occ) promoteRdrName (Orig _ _) = Nothing promoteRdrName (Exact _) = Nothing
As you can see,
Unqual
is promoted toUnqual
andQual
is promoted toQual
, so we can't mix those up. Let's consider our cases (1) and (2) again
f (Just @a a) = vfun a
Here we haveUnqual (OccName VarName "a")
, which is promoted toUnqual (OccName TvName "a")
. The promoted name can also be found in the environment, so we report a punf (Just @a a) = vfun OtherMod.a
Here we haveQual "OtherMod" (OccName VarName "a")
, which is promoted toQual "OtherMod" (OccName TvName "a")
. The promoted name is looked up inOtherMod
, so there is no pun.Now imagine we try to do the same thing in the type checker, but we only have the resolved
Name
. How can we promote its namespace? I tried thenameRdrName
helper, only to discover that it simply wraps the name asExact
.
Simon: !11166 (comment 533617)
I thought about it a bit in the interim. (You made some good points in your replies which are now invisible, sadly. I hope not lost.)
My main suggestion. Why not do this:
type instance XVar (GhcPass GhcRn) = RdrName
That is, with each occurrence, record the original
RdrName
, alongside theName
that the renamer has resolved it to.
- The
RdrName
is easily to hand (obviously)- It would support more accurate pretty-printing of the output of the renamer (or typechecker actually); e.g. whether it is qualified.
- In T2T you would have the info you needed
- You could even have
type instance XVar (GhcPass _) = RdrName
: same regardless of which pass.That seems less ad-hoc than your earlier version. No type class I hope!