Skip to content
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

fix array/set/tuple literals with generic expression elements #24497

Open
wants to merge 3 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 51 additions & 21 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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..<result.len:
result[i] = fitNode(c, typ, result[i], result[i].info)
if typ.kind == tyFromExpr and c.inGenericContext > 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..<result.len:
result[i] = fitNode(c, typ, result[i], result[i].info)
if constructType:
result.typ.setIndexType(
makeRangeType(c,
Expand Down Expand Up @@ -2793,7 +2802,9 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
var typ: PType = nil
for i in 0..<n.len:
let doSetType = typ == nil
if isRange(n[i]):
if not doSetType and typ.kind == tyFromExpr and c.inGenericContext > 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)
Expand All @@ -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):
Expand All @@ -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..<n.len:
var m: PNode
let info = n[i].info
if isRange(n[i]):
if typ.kind == tyFromExpr and c.inGenericContext > 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)
Expand Down Expand Up @@ -2937,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..<n.len:
let expectedElemType = if expected != nil: expected[i] else: nil
n[i] = semExprWithType(c, n[i], {}, expectedElemType)
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)
if typ.kind == tyFromExpr:
n[i] = semGenericStmt(c, n[i])
else:
n[i] = semExprWithType(c, n[i], {}, expectedElemType)
if n[i].typ != nil and n[i].typ.kind == tyFromExpr and c.inGenericContext > 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
Expand Down Expand Up @@ -3035,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
metagn marked this conversation as resolved.
Show resolved Hide resolved
var isTupleType: bool = false
if tupexp.len > 0: # don't interpret () as type
isTupleType = tupexp[0].typ.kind == tyTypeDesc
Expand Down
32 changes: 32 additions & 0 deletions tests/proc/tgenericdefaultparam.nim
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,35 @@ 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]()

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]()

Loading