From da8ce86ca57562185528ebfbd1beb295be293902 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Sun, 20 Aug 2023 22:13:30 +0200 Subject: [PATCH] generics: fix coercions to distinct phantom types (#848) ## Summary Fix phantom type information being lost in conversions-with-inference (i.e, `Typ(x)` where `Typ` is a generic type) to distinct types. ## Details Matching a `B[U]` against a `A[T] = distinct T`, which is what eventually happens for conversions like `A(B)`, results in the actual type `B[U]` being matched against the formal type `distinct T`. Since the invocation information (i.e., `tyGenericInst`) of the actual type is skipped when the formal type is a `tyDistinct`, phantom type information present on the instance was lost. Skipping the invocation information is now removed, and phantom types are thus passed on properly. --- compiler/sem/sigmatch.nim | 6 ++- tests/typerel/tcoercing_generic_distincts.nim | 43 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/typerel/tcoercing_generic_distincts.nim diff --git a/compiler/sem/sigmatch.nim b/compiler/sem/sigmatch.nim index b4a36d6e743..8bb30eb975c 100644 --- a/compiler/sem/sigmatch.nim +++ b/compiler/sem/sigmatch.nim @@ -1082,7 +1082,7 @@ typeRel can be used to establish various relationships between types: if a.kind == tyGenericInst and skipTypes(f, {tyStatic, tyVar, tyLent, tySink}).kind notin { - tyGenericBody, tyGenericInvocation, + tyGenericBody, tyGenericInvocation, tyDistinct, tyGenericInst, tyGenericParam} + tyTypeClasses: return typeRel(c, f, lastSon(a), flags) @@ -1368,7 +1368,9 @@ typeRel can be used to establish various relationships between types: inc(c.inheritancePenalty, depth) result = isSubtype of tyDistinct: - a = a.skipTypes({tyGenericInst, tyRange}) + # FIXME: don't skip ``tyRange`` here. A ``range[D(0) .. D(1)`` is not + # equal to ``D = distinct int`` + a = a.skipTypes({tyRange}) if a.kind == tyDistinct: if sameDistinctTypes(f, a): result = isEqual diff --git a/tests/typerel/tcoercing_generic_distincts.nim b/tests/typerel/tcoercing_generic_distincts.nim new file mode 100644 index 00000000000..35935ef4fde --- /dev/null +++ b/tests/typerel/tcoercing_generic_distincts.nim @@ -0,0 +1,43 @@ +discard """ + description: ''' + Ensure that types can be coerced into generic distinct types + ''' + action: compile +""" + +type + Tuple[A] = (int, int) # tuple with phantom type information + + Generic[T] = distinct T + Generic2[T] = distinct Tuple[T] + + Generic3[T] = distinct Tuple[int] + ## generic phantom distinct type of a phantom type + +template check(val: untyped, expect: untyped) = + static: doAssert typeof(val) is expect + +var x: Tuple[float] = (0, 0) + +# coercing a value to an explicitly distinct type works: +check Generic[int](0), Generic[int] +check Generic(int(0)), Generic[int] + +# coercing a value that is of phantom type to a distinct type that uses the +# phantom type information works: +check Generic2[float](x), Generic2[float] +check Generic2(x), Generic2[float] + +# check that the full instantiated type is bound in the "distinct of generic +# parameter" case +check typeof(Generic(x)).T.A, float + +static: + # XXX: issue unrelated to typerel. ``checkConvertible`` skips the relevant + # type information before calling ``cmpTypes`` + doAssert compiles(Generic2[int](x)), + "conversion-to-generics start working properly" + + # check that different phantom types are not treated as equal in + # to-distinct coercions + doAssert not compiles(Generic3(x))