-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
typerel: rework tyGenericInvocation
handling
#854
typerel: rework tyGenericInvocation
handling
#854
Conversation
In preparation for the upcoming changes, the code is adjusted to also support `tyGenericInvocation`s as both the formal and the actual type.
Instead of creating an improper instantiation to match `tyGenericInst` and `tyArray`s against, `tyGenericInvocation` are handled directly by `typrel`. The implementation attempts to capture the behaviour of the path taken through the `tyGenericInst` handling for the lifted instance.
The existing `replaceTypeVarsT` not only replaces type variables, it also resolves invocations. For properly implementing step-by-step matching against `tyGenericInvocation`, a procedure for only replacing type parameters is needed, which is what `replaceTypeParamsInType` does.
Go back to using opaque type-parameter forwarding, which fixes type parameters in the AST of ranges and type expressions not being replaced. However, instead of relying on `replaceTypeVarsT` or `prepareMetatypeForSigmatch`, the new `replaceTypeParamsInType` is used.
While pre-existing, the cause does change slightly due to the changes in `typeRel`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree the temporary regression is acceptable.
Found a non-blocking very minor typo, I wouldn't bother fixing it -- could easily be done in a future PR, and since it's in 'known issue' text might be removed entirely.
# XXX: this seems like a really bad idea, why should that work? The | ||
# procedure wants a typedesc for an instantiated type, but it gets | ||
# an (unlifted) composite-type-class |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't, pretty sure it should be an error where Generic[int]
is expected, but received a Generic
.
Specifying the type parameter on the procedure should affect the formal parameter's type definition and interpretation of any argument (the instantiated procedure's declaration), but not the actual argument.
|
||
# XXX: since we're forwarding the arguments here, the constraints on the | ||
# body's parameters are ignored... | ||
let other = replaceTypeParamsInType(c.c, bindings, body) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a temporary regression because, it's not unreasonable to do the check as part of the replace in the future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, my current plan goes into that direction. In short, the idea is to, should the typeRel
against the patched body return a match, perform a typeRel
for all type arguments against their passed-to parameter's constraint. For that to work, typeRel
is going to need some more improvements, which is why I haven't implemented it as part of this PR.
A complementary improvement could be leveraging the tyInferred
mechanism (or an improved/extended version thereof) to infer the constraints for a generic parameter on definition:
type Generic[A: object] = (A, A)
proc f[T](x: Generic[T]) = ...
here T
would be inferred to be constrained to an object
type. This would make it possible to detect conflicting constraints on definition of a generic type/routine (instead of during parameter type matching).
knownIssue: ''' | ||
The type variable in the range expression reaches the final | ||
``semtypinst.prepareNode`` call as something that is not detected as a | ||
type variables |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type variables | |
type variable |
# test with range: | ||
var a: range[7..10] | ||
doAssert f(3, a) == 8 | ||
# negative test: make sure that range types not overlapping are rejected |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these are super valuable, nice!
/merge |
Merge requested by: @zerbina Contents after the first section break of the PR description has been removed and preserved below:
|
Summary
Make the type relationship computation (
typeRel
) for instantiatedgeneric types (
tyGenericInst
) and unresolved type applications(
tyGenericInvocation
) more consistent with each other, which, apartfrom removing another dependency on
semtypinst
's "meta-types allowed"mode, is also a preparation for fixing the handling of phantom types.
Fixes:
Body[T] = T.param
andBody[N] = range[0..(N+1)]
with generic parameters in routinesignatures now work properly during parameter matching
proc f[T](x: Type[Type[T]])
and
Type[A] = (A,)
, inferringT
asX
now works when passinga
(Type[X],)
A temporary regression is that constraints of generic parameters are now
ignored when a non-generic-instance is matched against the invocation of
a generic type.
Details
Key changes:
typeRel
doesn't directly useprepareMetatypeForSigmatch
anymore,which is another step towards removing
semtypinst
's "meta-typesallowed" mode
replaceTypeVarsInType
procedure is added, which replaces typevariables without also resolving
tyGenericInvocation
styGenericInvocation
handling intypeRel
is now much more similarto that of
tyGenericInst
When computing the relationship with a
tyGenericInvocation
(e.g., aType[T]
in a routine signature whereT
is a generic parameter) and:(i.e.,
tyGenericInst
)typeRel
happens in the context of routine signature parameter typematching
the invocation was first resolved via
prepareMetatypeForSigmatch
andthe argument type then matched against the instantiated type. The reason
for this seems to have been being able to consistently handle generic
aliases (by skipping them) and, in the case of
array
s, that staticparameter inference works.
What
prepareMetatypeForSigmatch
does is create a fully instantiatedtyGenericInst
for atyGenericInvocation
, using the "meta-typesallowed" mode of
semtypinst
. For example, the instance produced forBody[A] = (A, A)
with an invocation likeBody[T]
would be a(T, T)
type.
Reworked handling
In order to not having to rely on the "meta-types allowed" mode, the
handling of
tyGenericInvocation
is reworked to be the following (f
is the formal type and
a
the argument type after generic alias areskipped):
a
is atyGenericInvocation
, the behaviour is the same aspreviously
a
is atyGenericInst
and it's an instance of the generic typethat the invocation invokes, their relation is that of their
arguments (the comparison works the same as for two
tyGenericInst
)object
type (includingref
andptr
versions),f
anda
have a sub-type relationship ifa
is asub-type of
f
, otherwise there's no relationf
is an invocation of aand|or
type or of anotherinvocation and
a
is atyGenericInst
, there is no relationship(this mirrors the
tyGenericInst
-against-tyGenericInst
handling)f
's body are replaced with the types passed to the invocation, anda
is matched against the resulting typeThe replacing in step 5. is implemented by the new
replaceTypeParamsInType
procedure, which is similar toreplaceTypeVarsT
, but with the important difference that:tyGenericInvocation
styFromExpr
, by also replacing the typeparameters in the attached AST
this is not needed for
typeRel
to work with the typesFor the case where
a
is not atyGenericInst
ortyArray
, theimportant difference is that
a
is now not matched against the body off
first, with the inferred parameters off
then being matchedagainst its arguments. This leads to inference working in more complex
cases, but it also means that the parameter constraints of the invoked
types are ignored. For example:
doesn't fail now, while it previously (correctly) did.
Related changes:
tyGenericInst
types into a standalone procedure and adjust it to also work with
tyGenericInvocation
sskipToObject
to not skip overtyGenericBody
, as doingso is almost never what is wanted
isSubtypeOfGenericInstance
to supportf
being atyGenericInvocation
isGenericSubtype
types