From f1048be5f7ac81cc23119ebc0efda76f7b0d0f9c Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 3 Dec 2024 08:04:19 +0300 Subject: [PATCH 1/3] fix array/set literals with generic expression elements fixes #24484 --- compiler/semexprs.nim | 45 ++++++++++++++++++++--------- tests/proc/tgenericdefaultparam.nim | 22 ++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index bac00bbee1300..6d884830799bd 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -725,7 +725,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp # nkBracket nodes can also be produced by the VM as seq constant nodes # in which case, we cannot produce a new array type for the node, # as this might lose type info even when the node has array type - let constructType = n.typ.isNil + let constructType = n.typ.isNil or n.typ.kind == tyFromExpr var expectedElementType, expectedIndexType: PType = nil var expectedBase: PType = nil if constructType: @@ -797,17 +797,26 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp localError(c.config, x.info, "invalid order in array constructor") x = x[1] - let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType) - result.add xx - if constructType: - typ = commonType(c, typ, xx.typ) + if typ.kind == tyFromExpr and c.inGenericContext > 0: + result.add semGenericStmt(c, x) + else: + let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType) + result.add xx + if constructType: + typ = commonType(c, typ, xx.typ) #n[i] = semExprWithType(c, x, {}) #result.add fitNode(c, typ, n[i]) inc(lastIndex) - if constructType: - addSonSkipIntLit(result.typ, typ, c.idgen) - for i in 0.. 0: + if constructType: + result.typ() = nil # current result.typ is invalid, index type is nil + result.typ() = makeTypeFromExpr(c, result.copyTree) + return + else: + if constructType: + addSonSkipIntLit(result.typ, typ, c.idgen) + for i in 0.. 0: + n[i] = semGenericStmt(c, n[i]) + elif isRange(n[i]): checkSonsLen(n[i], 3, c.config) n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType) n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType) @@ -2811,7 +2822,10 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode = if doSetType: typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) if doSetType: - if not isOrdinalType(typ, allowEnumWithHoles=true): + if typ.kind == tyFromExpr and c.inGenericContext > 0: + # propagate it as set element type + discard + elif not isOrdinalType(typ, allowEnumWithHoles=true): localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc)) typ = makeRangeType(c, 0, MaxSetElements-1, n.info) elif isIntLit(typ): @@ -2825,11 +2839,16 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode = typ = makeRangeType(c, 0, MaxSetElements-1, n.info) if expectedElementType == nil: expectedElementType = typ - addSonSkipIntLit(result.typ, typ, c.idgen) + if typ.kind == tyFromExpr and c.inGenericContext > 0: + result.typ() = makeTypeFromExpr(c, result.copyTree) + else: + addSonSkipIntLit(result.typ, typ, c.idgen) for i in 0.. 0: + m = n[i] + elif isRange(n[i]): m = newNodeI(nkRange, info) m.add fitNode(c, typ, n[i][1], info) m.add fitNode(c, typ, n[i][2], info) diff --git a/tests/proc/tgenericdefaultparam.nim b/tests/proc/tgenericdefaultparam.nim index 7bce591ce5ee2..d20af411ed1eb 100644 --- a/tests/proc/tgenericdefaultparam.nim +++ b/tests/proc/tgenericdefaultparam.nim @@ -96,3 +96,25 @@ block: # issue #24121 proc baz[T: FooBar](x: T, y = foo(x)): string = y doAssert baz(Foo(123)) == "b" doAssert baz(Bar(123)) == "c" + +block: # issue #24484 + type E = enum A + proc foo[T](t: set[T] = {T.A}) = + discard + foo[E]() + + proc bar[T](t: set[T] = {T(0), 5}) = + doAssert t == {0, 5} + bar[uint8]() + doAssert not compiles(bar[string]()) + +block: # issue #24484, array version + type E = enum A + proc foo[T](t: openArray[T] = [T.A]) = + discard + foo[E]() + + proc bar[T](t: openArray[T] = [T(0), 5]) = + doAssert t == [T(0), 5] + bar[uint8]() + From 3f6ca899da88e648a55760f5694e912d817899b6 Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 3 Dec 2024 08:28:48 +0300 Subject: [PATCH 2/3] also add tuples --- compiler/semexprs.nim | 27 +++++++++++++++++++-------- tests/proc/tgenericdefaultparam.nim | 10 ++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6d884830799bd..198c6c58f1858 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2956,14 +2956,20 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT var typ = newTypeS(tyTuple, c) # leave typ.n nil! for i in 0.. 0: + typ = makeTypeFromExpr(c, n.copyTree) + else: + if expectedElemType != nil and + (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)): + # hasEmpty/nil check is to not break existing code like + # `const foo = [(1, {}), (2, {false})]`, + # `const foo = if true: (0, nil) else: (1, new(int))` + n[i] = fitNode(c, expectedElemType, n[i], n[i].info) + addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen) result.typ() = typ include semobjconstr @@ -3054,6 +3060,11 @@ proc semExport(c: PContext, n: PNode): PNode = proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = var tupexp = semTuplePositionsConstr(c, n, flags, expectedType) + # convert `tupexp` to typedesc if necessary: + if tupexp.typ.kind == tyFromExpr: + # tyFromExpr is already ambivalent between types and values + result = tupexp + return var isTupleType: bool = false if tupexp.len > 0: # don't interpret () as type isTupleType = tupexp[0].typ.kind == tyTypeDesc diff --git a/tests/proc/tgenericdefaultparam.nim b/tests/proc/tgenericdefaultparam.nim index d20af411ed1eb..04f0774308f93 100644 --- a/tests/proc/tgenericdefaultparam.nim +++ b/tests/proc/tgenericdefaultparam.nim @@ -118,3 +118,13 @@ block: # issue #24484, array version doAssert t == [T(0), 5] bar[uint8]() +block: # issue #24484, tuple version + type E = enum A + proc foo[T](t = (T.A,)) = + discard + foo[E]() + + proc bar[T](t: (T, int) = (T(0), 5)) = + doAssert t == (T(0), 5) + bar[uint8]() + From efab0c8aa5708d5db879a81b716420fef4b79544 Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 3 Dec 2024 09:14:23 +0300 Subject: [PATCH 3/3] Update compiler/semexprs.nim Co-authored-by: Andreas Rumpf --- compiler/semexprs.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 198c6c58f1858..46de230f23a5d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -3063,8 +3063,7 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp # convert `tupexp` to typedesc if necessary: if tupexp.typ.kind == tyFromExpr: # tyFromExpr is already ambivalent between types and values - result = tupexp - return + return tupexp var isTupleType: bool = false if tupexp.len > 0: # don't interpret () as type isTupleType = tupexp[0].typ.kind == tyTypeDesc