forked from nim-works/nimskull
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sigmatch: simpler procedural type matching
Summary ======= * simplify the logic for handling generic routines using `auto` as the return types * reject un-instantiated routines earlier when used in branch and array constructor expressions, resulting in clearer error messages (`has no concrete type` instead of `cannot instantiate`) * support lambda expressions coming from template expansions Details ======= Introduced by nim-lang/Nim#3234, when both the formal and actual type are unresolved meta types, the relation is considered to be `isBothMetaConvertible`. The intention seems to have been supporting passing `auto`-returning routines or lambda-expressions to generic procedure-type parameters, but the logic introduced by the aforementioned PR is not necessary for achieving that. `auto` return types now use dedicated handling in `procTypeRel`, both the formal and actual type being unresolved generics results in a mismatch again, and `typeRel` is no longer applied repeatedly for `isBothMetaConvertible` matches. There was also the issue of `generateTypeInstance` being misused, which could result in spurious `cannot instantiate` or internal compiler errors. Since meta types are possibly involved, the correct routine to use is `prepareMetatypeForSigmatch`. Testing that the produced type instance is not a meta type was also done incorrectly (`isMetaType` was used instead of `containsGenericType`). Finally, `nkStmtListExpr` nodes are now considered when instantiating a generic routine as part of parameter matching, which allows for complex expressions yielding un-instantiated generic routines (e.g.: `call((discard x; genericRoutine))`).
Showing
8 changed files
with
223 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
tests/lang_callable/generics/tgeneric_intantiation_outside_call.nim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
tests/lang_callable/generics/tgeneric_procedure_cannot_assigned.nim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
discard """ | ||
description: ''' | ||
Tests for inference of parameter types of procedural types during overload | ||
resolution | ||
''' | ||
""" | ||
|
||
block inferred_actual_type: | ||
# passing a generic routine to a parameter of concrete procedural type | ||
# works; the types are inferred and an instantiation created | ||
proc call(x: proc(x: int): bool) = | ||
doAssert x(1) == true | ||
|
||
proc generic[A, B](x: A): B = | ||
result = true | ||
|
||
call(generic) | ||
# also works for lambda expressions: | ||
call(proc(x: auto): bool = true) | ||
|
||
block inferred_actual_type_late_bound: | ||
# inference also works when the formal type is a type variable that has a | ||
# type bound already | ||
proc call[T](a: T, x: proc(x: T): bool) = | ||
doAssert x(a) == true | ||
|
||
proc generic[A, B](x: A): B = | ||
result = true | ||
|
||
call(1, generic) | ||
# also works for lambda expressions: | ||
call(2, proc(x: auto): bool = true) | ||
|
||
block cross_inference: | ||
# the inferred type is bound to the type variable of the actual parameter, | ||
# meaning that each further usage of the type variable acts like the type it | ||
# was inferred as | ||
proc call[A, B](val: A, x: proc(a: A, b: B): bool) = | ||
doAssert x(val, val) == true | ||
|
||
proc generic[A](a: A, b: A): bool = | ||
result = true | ||
|
||
call(1, generic) | ||
# a lambda expression with two parameters using the same type variable | ||
# (i.e., generic parameter) doesn't work | ||
|
||
block more_complex_inference: | ||
# inference also works when both the formal and actual type are not directly | ||
# generic parameters | ||
type | ||
Container[T] = object | ||
Alias[U] = Container[U] | ||
|
||
proc call[T](val: T, x: proc(a: Container[T]): bool) = | ||
doAssert x(Container[T]()) == true | ||
|
||
proc generic[T](a: Alias[T]): bool = | ||
result = true | ||
|
||
call(1, generic) | ||
|
||
block auto_return_type: | ||
# a generic routine using an 'auto' return type can also be passed to a | ||
# parameter of procedural type. All type variables of the input procedural | ||
# type are inferred first, and then the routine is instantiated. After | ||
# that, the type of the instantiated routine is matched against the formal | ||
# type | ||
proc callSimple(val: int, x: proc(x: int): int) = | ||
doAssert x(val) == val | ||
|
||
proc callGenericRet[T](val: int, x: proc(x: int): T) = | ||
# the return type of the procedural type is generic | ||
doAssert x(val) == val | ||
|
||
proc generic[T](x: T): auto = | ||
result = x | ||
|
||
callSimple(1, generic) | ||
callGenericRet(2, generic) | ||
# lambda expressions also support the auto return type | ||
callSimple(3, proc(x: auto): auto = x) | ||
callGenericRet(3, proc(x: auto): auto = x) | ||
|
||
block complex_argument_expression_with_auto_return_type: | ||
# complex expressions yielding generic routines also work | ||
proc call(val: int, x: proc(x: int): int) = | ||
doAssert x(val) == val | ||
|
||
proc generic[T](x: T): auto = | ||
result = x | ||
|
||
call(1, (discard ""; generic)) | ||
# also works with lambda expressions: | ||
call(2, (discard ""; (proc(x: auto): auto = x))) |