From 2d8114929450ad621a2ef0108bbf1267c45145fc Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Sun, 26 Jul 2026 12:08:44 -0400 Subject: [PATCH] unwrap typedesc in semSet to enable stuff like `set[T.distinctBase]` (#25924) `distinctBase` results in typedesc, so `set[T.distinctBase]` received `typedesc[range[...]]` as its element type, which `isOrdinalType` rejects. Strip the wrapper in `semSet` before storing the element type and checking ordinality. Also add `tyFromExpr` to the deferred-check set so the error doesn't fire prematurely inside generic bodies - same pattern already used by `semArray`. --- compiler/semtypes.nim | 3 ++- tests/set/tset_range_trait.nim | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/set/tset_range_trait.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index a026fc994a..1c4c81c64c 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -219,9 +219,10 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tySet, prev, c) if n.len == 2 and n[1].kind != nkEmpty: var base = semTypeNode(c, n[1], nil) + if base.kind == tyTypeDesc: base = base.base # unwrap from type traits like distinctBase addSonSkipIntLit(result, base, c.idgen) if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base) - if base.kind notin {tyGenericParam, tyGenericInvocation}: + if base.kind notin {tyGenericParam, tyGenericInvocation, tyFromExpr}: if base.kind == tyForward: c.forwardTypeUpdates.add (getCurrOwner(c), result, n) elif not isOrdinalType(base, allowEnumWithHoles = true): diff --git a/tests/set/tset_range_trait.nim b/tests/set/tset_range_trait.nim new file mode 100644 index 0000000000..0eafdca1f8 --- /dev/null +++ b/tests/set/tset_range_trait.nim @@ -0,0 +1,39 @@ +# Test that set[] accepts range types via typedesc[R], and set[typedesc[R]] +# must unwrap the typedesc wrapper before checking ordinality. + +import std/typetraits + +type + TestDistinctRange = distinct range[0 .. 63] + +block: # explicit range type as set base + type S = set[range[0 .. 63]] + var s: S = {0, 1} + doAssert 0 in s + +block: # distinctBase result as set base (non-generic) + type S = set[TestDistinctRange.distinctBase] + var s: S = {0, 1} + doAssert 0 in s + +block: # range alias as set base + type RangeAlias = range[0 .. 63] + type S = set[RangeAlias] + var s: S = {0, 1} + doAssert 0 in s + +block: # set[T.distinctBase] in generic body type position + proc test[T: TestDistinctRange]() = + var s: set[T.distinctBase] + s = {0, 1} + doAssert 0 in s + + test[TestDistinctRange]() + +block: # passing set[T.distinctBase] to a proc expecting set[0..63] + proc accept(x: typedesc[set[0 .. 63]]) = discard + + proc pass[T: TestDistinctRange](p: typedesc[set[T]]) = + accept(set[T.distinctBase]) + + pass(set[TestDistinctRange])