-
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
better dependent-expression support for range types #850
better dependent-expression support for range types #850
Conversation
Range expressions and expressions in the array-index type position are now first sem'ed as generic expressions, and only if they don't depend on type variables are they fully semantically analyzed. This is going to require some changes to the type variables replacing logic in `semtypinst`. As a preparation for this, the AST coming out of the generic pre-pass is pre-processed to have symbols bound for all type variables.
In addition, the test labeled `tsharedcases` is changed back to its correct original title `tgenericshardcases`.
The PR message was quite the mess, sorry for that. I've cleaned it up a bit, and hopefully it's easier to read and parse now. Looking ahead, the new |
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.
The way it works now is quite a bit better. Only some non-blocking suggestions.
Test coverage seems good. I tried to come up with some meaningful range
tests to suggest, but nothing particularly novel occurred to me. The best I could come up with is:
proc r(t: typedesc, rng: range[-typeNameLen(t), typeNameLen(t]): int =
result = rng.type.high - rng.type.low
doAssert r(MyType, 0) == 12
## Computes the correct type to assign to the symbol node of a replaced | ||
## type variable. |
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.
## Computes the correct type to assign to the symbol node of a replaced | |
## type variable. | |
## Computes the correct type to assign to the symbol of a replaced type | |
## variable. |
not sure if that's too pendantic, but we're technically working with the symbol and not an nkSym
.
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, we're working with the symbol, but the returned type is meant to be assigned to the node, hence the explicit mention.
@@ -1,5 +1,5 @@ | |||
discard """ | |||
errormsg: "\'vectFunc\' doesn't have a concrete type, due to unspecified generic parameters." | |||
errormsg: "type mismatch: got <proc (x: vector[vectFunc.T]): vector[vectFunc.T], vector[3]>" |
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.
this is really nice.
thanks for the additional edits, in fairness i found the earlier version was already pretty good, but the improvement is appreciated all the same.
considering i'd like to lean on Awesome PR. |
Thanks a lot for the review, @saem. Adding a test for |
Doesn't work yet, due to unrelated issues with `typeRel`.
Co-authored-by: Saem Ghani <[email protected]>
/merge |
Merge requested by: @zerbina Contents after the first section break of the PR description has been removed and preserved below:
|
Summary
Fix complex type-variable-dependent expressions not working reliably
for range and array-index types used in routine signatures.
Internally, expressions in both contexts are now first treated as
generic, and using
replaceTypesInBody
for expressions withpotentially unbound type variables is removed.
Language changes:
untyped
) used in range- and index-type expression are expandedbefore all further analysis (and thus typed macros/template, static
expressions, etc.)
depending on type variables are potentially expanded multiple times
Fixes:
signatures can now be arbitrarily complex (refer to the added tests
for an example of what is now possible)
Details
Overview:
now use the generic pre-pass
now always generic expressions (i.e., symbols are looked up, but
not AST is not typed)
replaceTypesInBody
is renamed toinstantiateTypesInBody
andsupport for unresolved type variables is removed -- the new
replaceTypeVarsInBody
now has to be used for that caseSemantic analysis has very basic, scattered support for expressions
depending on unresolved type variables, allowing
semExprWithType
tobe used for typing expressions in range and array type constructors.
However, in more complex cases, this usually results in errors with
unclear descriptions. Until more complete support for typing expressions
depending on unresolved type variables is implemented, it is more
robust to treat them as fully generic.
Analyzing the expressions
Both
semRangeAux
andsemArrayIndex
now always run the generic pre-pass first (
semGenericStmt
, which also expands syntax macros), whichis required for detecting whether unresolved type variables are used.
When no unresolved type variables exist in the analyzed expression, it
is directly passed on to
semExprWithType
.The procedure for querying whether a symbol is that of an unresolved
type variable (
isUnresolvedSym
), is moved to theast_query
module.
Looking for unresolved type variables is combined with making sure that
all type variables are referenced in the AST via their symbols
(
fixupTypeVars
) -- something that is not always already the case. Sometype variables not having their symbol bound would mean that the later
pass for replacing type variables cannot detect them.
As an unintended side-effect of using
semGenericStmt
, unrelatedsymbols are marked as used, which is what causes the different error
message in
tgeneric_backtrack_type_inference.nim
(the presence of thesfUsed
flag for generic parameters of routines decides whether aroutine is marked with
tfUnresolved
).Processing the generic expressions
For processing generic expressions used in the context of
static
parameter inference, where unbound type variables are possible, the
replaceTypeVarsInBody
procedure is introduced. While usingreplaceTypesInBody
withallowMetaTypes = true
would have sufficed,this is intended as a step towards removing the "meta-types allowed"
mode from
semtypinst
.Implementation-wise,
replaceTypeVarsInBody
is similar toprepareNode
,with the differences being that
replaceTypeVarsInBody
:Expression already having a type are not analyzed again when used as call
operands, and thus two compromises are required for the generic-expression
approach to work:
prepareNode
andreplaceTypeVarsInBody
have to mirrorhow
semSym
assigns types to symbol nodes of type variables (fixType
implements this)
replaceTypeVarsInBody
has to remove from the expression the typespreviously assigned by both
semRangeAux
andsemArrayIndex
replaceTypesInBody
is renamed toinstantiateTypesInBody
. In addition,the unnecessarily exported
replaceTypeVarsN
is un-exported.Misc
tgenericshardcases
test, which waserroneously changed to
tsharedcases
during a test suite cleanup