529 changes: 357 additions & 172 deletions compiler/concepts.nim

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions compiler/dfa.nim
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,8 @@ proc gen(c: var Con; n: PNode) =
genUse(c, n)
of nkIfStmt, nkIfExpr: genIf(c, n)
of nkWhenStmt:
# This is "when nimvm" node. Chose the first branch.
gen(c, n[0][1])
# This is "when nimvm" node. Chose the second branch.
gen(c, n[1][0])
of nkCaseStmt: genCase(c, n)
of nkWhileStmt: genWhile(c, n)
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
Expand Down
11 changes: 7 additions & 4 deletions compiler/docgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1406,11 +1406,14 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
for it in n: traceDeps(d, it)
of nkExportStmt:
for it in n:
# bug #23051; don't generate documentation for exported symbols again
if it.kind == nkSym and sfExported notin it.sym.flags:
if d.module != nil and d.module == it.sym.owner:
generateDoc(d, it.sym.ast, orig, config, kForceExport)
if it.kind == nkSym:
if d.module != nil and d.module == it.sym.owner: # in current module
# bug #23051; don't generate documentation for exported symbols again
if sfExported notin it.sym.flags:
generateDoc(d, it.sym.ast, orig, config, kForceExport)
# else it's to be handled in `of XxxSection` branch
elif it.sym.ast != nil:
# only export symbols in imported modules, not in current module
exportSym(d, it.sym)
of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
Expand Down
4 changes: 4 additions & 0 deletions compiler/enumtostr.nim
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
#newIntTypeNode(nkIntLit, field.position, t)
# safety branch for invalid data:
caseStmt.add newTree(nkElse,
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res),
newStrNode("", info))))

body.add(caseStmt)

Expand Down
9 changes: 7 additions & 2 deletions compiler/extccomp.nim
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ compiler tcc:
linkerExe: "tcc",
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
includeCmd: " -I",
linkDirCmd: "", # XXX: not supported yet
linkLibCmd: "", # XXX: not supported yet
linkDirCmd: " -L",
linkLibCmd: " -l$1",
debug: " -g ",
pic: "",
asmStmtFrmt: "asm($1);$n",
Expand Down Expand Up @@ -474,6 +474,11 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
result = conf.compileOptions

if (conf.cCompiler == ccGcc or conf.cCompiler == ccCLang) and
conf.selectedGC == gcRefc:
# bug #10625
addOpt(result, "-fno-omit-frame-pointer")

for option in conf.compileOptionsCmd:
if strutils.find(result, option, 0) < 0:
addOpt(result, option)
Expand Down
9 changes: 6 additions & 3 deletions compiler/injectdestructors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode =
result = genOp(c, op, n)
else:
result = newNodeI(nkCall, n.info)
result.add(newSymNode(createMagic(c.graph, c.idgen, "`=wasMoved`", mWasMoved)))
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
result.add copyTree(n) #mWasMoved does not take the address
#if n.kind != nkSym:
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
Expand Down Expand Up @@ -1197,8 +1197,11 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, flags, isFromSink = false)
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
if IsExplicitSink in flags:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
else:
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock:
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
# We know the result will be a stmt so we use that fact to optimize
handleNestedTempl(ri, process, willProduceStmt = true)
Expand Down
2 changes: 1 addition & 1 deletion compiler/lambdalifting.nim
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ proc interestingVar(s: PSym): bool {.inline.} =
proc illegalCapture(s: PSym): bool {.inline.} =
result = classifyViewType(s.typ) != noView or s.kind == skResult

proc isInnerProc(s: PSym): bool =
proc isInnerProc*(s: PSym): bool =
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
result = s.skipGenericOwner.kind in routineKinds
else:
Expand Down
11 changes: 10 additions & 1 deletion compiler/layeredtable.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import std/tables
import std/[tables]
import ast

type
Expand Down Expand Up @@ -53,6 +53,15 @@ proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
let tmp = pt.nextLayer[]
pt = tmp

iterator pairs*(pt: LayeredIdTable): (ItemId, PType) =
var tm = pt
while true:
for (k, v) in pairs(tm.topLayer):
yield (k, v)
if tm.nextLayer == nil:
break
tm.setToPreviousLayer

proc lookup(typeMap: ref LayeredIdTableObj, key: ItemId): PType =
result = nil
var tm = typeMap
Expand Down
53 changes: 29 additions & 24 deletions compiler/liftdestructors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)

proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
info: TLineInfo; idgen: IdGenerator): PSym

proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
idgen: IdGenerator)
Expand Down Expand Up @@ -91,7 +91,7 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
call.typ() = t
body.add newAsgnStmt(x, call)
elif c.kind == attachedWasMoved:
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
body.add genBuiltin(c, mWasMoved, "wasMoved", x)

proc genAddr(c: var TLiftCtx; x: PNode): PNode =
if x.kind == nkHiddenDeref:
Expand Down Expand Up @@ -148,7 +148,7 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
if sfNeverRaises notin op.flags:
c.canRaise = true
if c.addMemReset:
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "`=wasMoved`", x))
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "wasMoved", x))
else:
result = destroy

Expand All @@ -168,7 +168,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
defaultOp(c, f.typ, body, x.dotField(f), b)
else:
if enforceWasMoved:
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
body.add genBuiltin(c, mWasMoved, "wasMoved", x.dotField(f))
fillBody(c, f.typ, body, x.dotField(f), b)
of nkNilLit: discard
of nkRecCase:
Expand Down Expand Up @@ -277,7 +277,8 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
#body.add newAsgnStmt(blob, x)

var wasMovedCall = newNodeI(nkCall, c.info)
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "`=wasMoved`", mWasMoved)))
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))

wasMovedCall.add x # mWasMoved does not take the address
body.add wasMovedCall

Expand Down Expand Up @@ -612,7 +613,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if canFormAcycle(c.g, t.elemType):
# follow all elements:
forallElements(c, t, body, x, y)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)

proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
Expand Down Expand Up @@ -650,7 +651,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if op == nil:
return # protect from recursion
body.add newHookCall(c, op, x, y)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
# XXX: replace these with assertions.
let op = getAttachedOp(c.g, t, c.kind)
Expand All @@ -672,7 +673,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genBuiltin(c, mDestroy, "destroy", x)
of attachedTrace:
discard "strings are atomic and have no inner elements that are to trace"
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)

proc cyclicType*(g: ModuleGraph, t: PType): bool =
case t.kind
Expand Down Expand Up @@ -771,7 +772,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# If the ref is polymorphic we have to account for this
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
#echo "can follow ", elemType, " static ", isFinal(elemType)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
if isCyclic:
body.add newAsgnStmt(x, y)
Expand Down Expand Up @@ -838,7 +839,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace:
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)

proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
Expand Down Expand Up @@ -866,7 +867,7 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.sons.insert(des, 0)
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)

proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
var actions = newNodeI(nkStmtList, c.info)
Expand Down Expand Up @@ -894,7 +895,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genIf(c, x, actions)
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)

proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if c.kind == attachedDeepCopy:
Expand Down Expand Up @@ -934,7 +935,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.sons.insert(des, 0)
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)

proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
Expand All @@ -952,7 +953,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genIf(c, xx, actions)
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)

proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case t.kind
Expand Down Expand Up @@ -1002,9 +1003,13 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# 'selectedGC' here to determine if we have the new runtime.
discard considerUserDefinedOp(c, t, body, x, y)
elif tfHasAsgn in t.flags:
# seqs with elements using custom hooks in refc
if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
body.add newSeqCall(c, x, y)
forallElements(c, t, body, x, y)
if c.kind == attachedWasMoved:
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
else:
forallElements(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyString:
Expand All @@ -1021,7 +1026,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of {attachedAsgn, attachedSink, attachedDup}:
body.add newAsgnStmt(x, y)
of attachedWasMoved:
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
else:
fillBodyObjT(c, t, body, x, y)
else:
Expand Down Expand Up @@ -1063,9 +1068,7 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
assert typ.kind == tyDistinct
let baseType = typ.elementType
if getAttachedOp(g, baseType, kind) == nil:
# TODO: fixme `isDistinct` is a fix for #23552; remove it after
# `-d:nimPreviewNonVarDestructor` becomes the default
discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
discard produceSym(g, c, baseType, kind, info, idgen)
result = getAttachedOp(g, baseType, kind)
setAttachedOp(g, idgen.module, typ, kind, result)

Expand Down Expand Up @@ -1104,7 +1107,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache
incl result.flags, sfGeneratedOp

proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym =
if kind == attachedDup:
return symDupPrototype(g, typ, owner, kind, info, idgen)

Expand All @@ -1115,7 +1118,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
idgen, result, info)

if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)):
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence})):
dest.typ = typ
else:
dest.typ = makeVarType(typ.owner, typ, idgen)
Expand Down Expand Up @@ -1157,13 +1160,13 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add newAsgnStmt(xx, yy)

proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
info: TLineInfo; idgen: IdGenerator): PSym =
if typ.kind == tyDistinct:
return produceSymDistinctType(g, c, typ, kind, info, idgen)

result = getAttachedOp(g, typ, kind)
if result == nil:
result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
result = symPrototype(g, typ, typ.owner, kind, info, idgen)

var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
fn: result)
Expand Down Expand Up @@ -1197,7 +1200,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
fillStrOp(a, typ, result.ast[bodyPos], d, src)
else:
fillBody(a, typ, result.ast[bodyPos], d, src)
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not lacksMTypeField(typ):
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(typ):
# bug #19205: Do not forget to also copy the hidden type field:
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)

Expand All @@ -1207,6 +1210,8 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
result.ast[pragmasPos].add newTree(nkExprColonExpr,
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))

if kind == attachedDestructor:
incl result.options, optQuirky
completePartialOp(g, idgen.module, typ, kind, result)


Expand Down
2 changes: 2 additions & 0 deletions compiler/lineinfos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ type
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
hintUnknownRaises = "UnknownRaises",
hintMsgOrigin = "MsgOrigin", # since 1.3.5
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1

Expand Down Expand Up @@ -236,6 +237,7 @@ const
hintUser: "$1",
hintUserRaw: "$1",
hintExtendedContext: "$1",
hintUnknownRaises: "$1 is a forward declaration without explicit .raises, assuming it can raise anything",
hintMsgOrigin: "$1",
hintDeclaredLoc: "$1"
]
Expand Down
52 changes: 49 additions & 3 deletions compiler/llstream.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import
pathutils

import std/strutils
when defined(nimPreviewSlimSystem):
import std/syncio

Expand Down Expand Up @@ -86,14 +86,57 @@ const
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
'|', '%', '&', '$', '@', '~', ','}
AdditionalLineContinuationOprs = {'#', ':', '='}
LineContinuationTokens = [
"let", "var", "const", "type", # section
"object", "tuple",
# from ./layouter.oprSet
"div", "mod", "shl", "shr", "in", "notin", "is",
"isnot", "not", "of", "as", "from", "..", "and", "or", "xor",
] # must be all `nimIdentNormalized`-ed

proc eqIdent(a, bNormalized: string): bool =
a.nimIdentNormalize == bNormalized

proc endsWithIdent(s, subs: string): bool =
let le = subs.len
if le > s.len: return false
s[^le .. ^1].eqIdent subs

proc continuesWithIdent(s, subs: string, start: int): bool =
s.substr(start, start+subs.high).eqIdent subs

proc endsWithIdent(s, subs: string, endIdx: var int): bool =
endIdx.dec subs.len
result = s.continuesWithIdent(subs, endIdx+1)

proc containsObjectOf(x: string): bool =
const sep = ' '
var idx = x.rfind(sep)
if idx == -1: return
template eatWord(word) =
while x[idx] == sep: idx.dec
result = x.endsWithIdent(word, idx)
if not result: return
eatWord "of"
eatWord "object"
result = true

proc endsWithLineContinuationToken(x: string): bool =
result = false
for tok in LineContinuationTokens:
if x.endsWithIdent(tok):
return true
result = x.containsObjectOf

proc endsWithOpr*(x: string): bool =
result = x.endsWith(LineContinuationOprs)

proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
result = inTripleString or line.len > 0 and (
line[0] == ' ' or
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) or
line.endsWithLineContinuationToken()
)

proc countTriples(s: string): int =
result = 0
Expand All @@ -109,7 +152,10 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
s.rd = 0
var line = newStringOfCap(120)
var triples = 0
while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
while true:
if not readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
# now readLineFromStdin meets EOF (ctrl-D/Z) or ctrl-C
quit()
s.s.add(line)
s.s.add("\n")
inc triples, countTriples(line)
Expand Down
4 changes: 1 addition & 3 deletions compiler/lookups.nim
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,11 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
else: handleError(n, origin)
result = getIdent(c.cache, id)
of nkOpenSymChoice, nkClosedSymChoice:
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
if n[0].kind == nkSym:
result = n[0].sym.name
else:
handleError(n, origin)
of nkOpenSym:
result = considerQuotedIdent(c, n[0], origin)
else:
handleError(n, origin)

Expand Down
8 changes: 8 additions & 0 deletions compiler/optimizer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ type
hasReturn, hasBreak: bool
label: PSym # can be nil
parent: ptr BasicBlock
symToDel: seq[PNode]

Con = object
somethingTodo: bool
inFinally: int

proc invalidateWasMoved(c: var BasicBlock; x: PNode)

proc nestedBlock(parent: var BasicBlock; kind: TNodeKind): BasicBlock =
BasicBlock(wasMovedLocs: @[], kind: kind, hasReturn: false, hasBreak: false,
label: nil, parent: addr(parent))
Expand Down Expand Up @@ -62,6 +65,10 @@ proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} =
if this.hasReturn:
parent.wasMovedLocs.setLen 0
parent.hasReturn = true
elif this.symToDel.len > 0:
parent.symToDel = this.symToDel
for i in this.symToDel:
invalidateWasMoved(parent, i)

proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool =
result = false
Expand Down Expand Up @@ -149,6 +156,7 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
# any usage of the location before destruction implies we
# cannot elide the 'wasMoved(x)':
b.invalidateWasMoved n
b.symToDel.add n

of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
Expand Down
4 changes: 4 additions & 0 deletions compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ type
## Useful for libraries that rely on local passC
jsNoLambdaLifting
## Old transformation for closures in JS backend
noPanicOnExcept
## don't panic on bare except

SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
Expand Down Expand Up @@ -402,6 +404,7 @@ type
projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
projectFull*: AbsoluteFile # projectPath/projectName
projectIsStdin*: bool # whether we're compiling from stdin
stdinFile*: AbsoluteFile # Filename to use in messages for stdin
lastMsgWasDot*: set[StdOrrKind] # the last compiler message was a single '.'
projectMainIdx*: FileIndex # the canonical path id of the main module
projectMainIdx2*: FileIndex # consider merging with projectMainIdx
Expand Down Expand Up @@ -578,6 +581,7 @@ proc newConfigRef*(): ConfigRef =
projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
projectFull: AbsoluteFile"", # projectPath/projectName
projectIsStdin: false, # whether we're compiling from stdin
stdinFile: AbsoluteFile"stdinfile",
projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
command: "", # the main command (e.g. cc, check, scan, etc)
commandArgs: @[], # any arguments after the main command
Expand Down
29 changes: 24 additions & 5 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -638,8 +638,9 @@ proc semiStmtList(p: var Parser, result: PNode) =
getTok(p)
if p.tok.tokType == tkParRi:
break
elif not (sameInd(p) or realInd(p)):
parMessage(p, errInvalidIndentation)
# ignore indent:
#elif not (sameOrNoInd(p) or realInd(p)):
# parMessage(p, errInvalidIndentation)
let a = complexOrSimpleStmt(p)
if a.kind == nkEmpty:
parMessage(p, errExprExpected, p.tok)
Expand Down Expand Up @@ -699,10 +700,12 @@ proc parsePar(p: var Parser): PNode =
asgn.add b
result.add(asgn)
if p.tok.tokType == tkSemiColon:
getTok(p)
semiStmtList(p, result)
elif p.tok.tokType == tkSemiColon:
# stmt context:
result.add(a)
getTok(p)
semiStmtList(p, result)
else:
a = colonOrEquals(p, a)
Expand Down Expand Up @@ -2107,12 +2110,28 @@ proc parseObjectCase(p: var Parser): PNode =
#| objectBranches = objectBranch (IND{=} objectBranch)*
#| (IND{=} 'elif' expr colcom objectPart)*
#| (IND{=} 'else' colcom objectPart)?
#| objectCase = 'case' declColonEquals ':'? COMMENT?
#| objectCase = 'case' (declColonEquals / pragma)? ':'? COMMENT?
#| (IND{>} objectBranches DED
#| | IND{=} objectBranches)
result = newNodeP(nkRecCase, p)
getTokNoInd(p)
var a = parseIdentColonEquals(p, {withPragma})
getTok(p)
if p.tok.tokType != tkOf:
# of case will be handled later
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
var a: PNode
if p.tok.tokType in {tkSymbol, tkAccent}:
a = parseIdentColonEquals(p, {withPragma})
else:
a = newNodeP(nkIdentDefs, p)
if p.tok.tokType == tkCurlyDotLe:
var prag = newNodeP(nkPragmaExpr, p)
prag.add(p.emptyNode)
prag.add(parsePragma(p))
a.add(prag)
else:
a.add(p.emptyNode)
a.add(p.emptyNode)
a.add(p.emptyNode)
result.add(a)
if p.tok.tokType == tkColon: getTok(p)
flexComment(p, result)
Expand Down
2 changes: 1 addition & 1 deletion compiler/patterns.nim
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ proc inSymChoice(sc, x: PNode): bool =
result = false
for i in 0..<sc.len:
if sc[i].sym == x.sym: return true
elif sc.kind == nkOpenSymChoice:
elif sc.kind in {nkOpenSymChoice, nkOpenSym}:
# same name suffices for open sym choices!
result = sc[0].sym.name.id == x.sym.name.id
else:
Expand Down
3 changes: 2 additions & 1 deletion compiler/pipelines.nim
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
result = moduleFromRodFile(graph, fileIdx, cachedModules)
let path = toFullPath(graph.config, fileIdx)
let filename = AbsoluteFile path
if fileExists(filename): # it could be a stdinfile
# it could be a stdinfile/cmdfile
if fileExists(filename) and not graph.config.projectIsStdin:
graph.cachedFiles[path] = $secureHashFile(path)
if result == nil:
result = newModule(graph, fileIdx)
Expand Down
35 changes: 22 additions & 13 deletions compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
return it[1]

proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
isStatement: bool = false)
isStatement: bool = false; comesFromPush = false)

proc recordPragma(c: PContext; n: PNode; args: varargs[string]) =
var recorded = newNodeI(nkReplayAction, n.info)
Expand Down Expand Up @@ -893,7 +893,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
if keyDeep:
localError(c.config, it.info, "user pragma cannot have arguments")

pragma(c, sym, userPragma.ast, validPragmas, isStatement)
pragma(c, sym, userPragma.ast, validPragmas, isStatement, comesFromPush)
n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
else:
Expand Down Expand Up @@ -947,15 +947,19 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wSize:
if sym.typ == nil: invalidPragma(c, it)
var size = expectIntLit(c, it)
case size
of 1, 2, 4:
sym.typ.size = size
sym.typ.align = int16 size
of 8:
sym.typ.size = 8
sym.typ.align = floatInt64Align(c.config)
if sfImportc in sym.flags:
# no restrictions on size for imported types
setImportedTypeSize(c.config, sym.typ, size)
else:
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
case size
of 1, 2, 4:
sym.typ.size = size
sym.typ.align = int16 size
of 8:
sym.typ.size = 8
sym.typ.align = floatInt64Align(c.config)
else:
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
of wAlign:
let alignment = expectIntLit(c, it)
if isPowerOfTwo(alignment) and alignment > 0:
Expand Down Expand Up @@ -1318,8 +1322,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
pragmaProposition(c, it)
of wEnsures:
pragmaEnsures(c, it)
of wEnforceNoRaises, wQuirky:
of wEnforceNoRaises:
sym.flags.incl sfNeverRaises
of wQuirky:
sym.flags.incl sfNeverRaises
if sym.kind in {skProc, skMethod, skConverter, skFunc, skIterator}:
sym.options.incl optQuirky
of wSystemRaisesDefect:
sym.flags.incl sfSystemRaisesDefect
of wVirtual:
Expand Down Expand Up @@ -1401,11 +1409,12 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
inc i

proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
isStatement: bool) =
isStatement: bool; comesFromPush = false) =
if n == nil: return
pragmaRec(c, sym, n, validPragmas, isStatement)
# XXX: in the case of a callable def, this should use its info
implicitPragmas(c, sym, n.info, validPragmas)
if not comesFromPush:
implicitPragmas(c, sym, n.info, validPragmas)

proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords,
isStatement: bool = false) =
Expand Down
69 changes: 37 additions & 32 deletions compiler/renderer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ proc atom(g: TSrcGen; n: PNode): string =
of nkEmpty: result = ""
of nkIdent: result = n.ident.s
of nkSym: result = n.sym.name.s
of nkClosedSymChoice, nkOpenSymChoice: result = n[0].sym.name.s
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym: result = n[0].sym.name.s
of nkStrLit: result = ""; result.addQuoted(n.strVal)
of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
Expand Down Expand Up @@ -565,8 +565,16 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkIfExpr:
result = lsub(g, n[0][0]) + lsub(g, n[0][1]) + lsons(g, n, 1) +
len("if_:_")
of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
of nkElseExpr: result = lsub(g, n[0]) + len("_else:_") # type descriptions
of nkElifExpr, nkElifBranch:
if isEmptyType(n[1].typ):
result = lsons(g, n) + len("elif_:_")
else:
result = lsons(g, n) + len("_elif_:_")
of nkElseExpr, nkElse:
if isEmptyType(n[0].typ):
result = lsub(g, n[0]) + len("else:_")
else:
result = lsub(g, n[0]) + len("_else:_") # type descriptions
of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n[0]) else: 0)+len("typeof()")
of nkRefTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ref")
of nkPtrTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ptr")
Expand Down Expand Up @@ -609,8 +617,6 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkCommentStmt: result = n.comment.len
of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
of nkImportAs: result = lsub(g, n[0]) + len("_as_") + lsub(g, n[1])
of nkElifBranch: result = lsons(g, n) + len("elif_:_")
of nkElse: result = lsub(g, n[0]) + len("else:_")
of nkFinally: result = lsub(g, n[0]) + len("finally:_")
of nkGenericParams: result = lcomma(g, n) + 2
of nkFormalParams:
Expand Down Expand Up @@ -1002,7 +1008,7 @@ type
proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
if renderIds notin g.flags:
case n.kind
of nkClosedSymChoice, nkOpenSymChoice:
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym:
if n.len > 0: result = bracketKind(g, n[0])
else: result = bkNone
of nkSym:
Expand Down Expand Up @@ -1415,10 +1421,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
of nkPrefix:
gsub(g, n, 0)
if n.len > 1:
let opr = if n[0].kind == nkIdent: n[0].ident
elif n[0].kind == nkSym: n[0].sym.name
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
else: nil
let opr = getPIdent(n[0])
let nNext = skipHiddenNodes(n[1])
if nNext.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
put(g, tkSpaces, Space)
Expand Down Expand Up @@ -1469,15 +1472,30 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
putWithSpace(g, tkColon, ":")
if n.len > 0: gsub(g, n[0], 1)
gsons(g, n, emptyContext, 1)
of nkElifExpr:
putWithSpace(g, tkElif, " elif")
gcond(g, n[0])
putWithSpace(g, tkColon, ":")
gsub(g, n, 1)
of nkElseExpr:
put(g, tkElse, " else")
putWithSpace(g, tkColon, ":")
gsub(g, n, 0)
of nkElifExpr, nkElifBranch:
if isEmptyType(n[1].typ):
optNL(g)
putWithSpace(g, tkElif, "elif")
gsub(g, n, 0)
putWithSpace(g, tkColon, ":")
gcoms(g)
gstmts(g, n[1], c)
else:
putWithSpace(g, tkElif, " elif")
gcond(g, n[0])
putWithSpace(g, tkColon, ":")
gsub(g, n, 1)
of nkElseExpr, nkElse:
if isEmptyType(n[0].typ):
optNL(g)
put(g, tkElse, "else")
putWithSpace(g, tkColon, ":")
gcoms(g)
gstmts(g, n[0], c)
else:
put(g, tkElse, " else")
putWithSpace(g, tkColon, ":")
gsub(g, n, 0)
of nkTypeOfExpr:
put(g, tkType, "typeof")
put(g, tkParLe, "(")
Expand Down Expand Up @@ -1739,19 +1757,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
of nkMixinStmt:
putWithSpace(g, tkMixin, "mixin")
gcomma(g, n, c)
of nkElifBranch:
optNL(g)
putWithSpace(g, tkElif, "elif")
gsub(g, n, 0)
putWithSpace(g, tkColon, ":")
gcoms(g)
gstmts(g, n[1], c)
of nkElse:
optNL(g)
put(g, tkElse, "else")
putWithSpace(g, tkColon, ":")
gcoms(g)
gstmts(g, n[0], c)
of nkFinally, nkDefer:
optNL(g)
if n.kind == nkFinally:
Expand Down
2 changes: 1 addition & 1 deletion compiler/reorder.nim
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
of nkIdent: uses.incl n.ident.id
of nkSym: uses.incl n.sym.name.id
of nkAccQuoted: uses.incl accQuoted(cache, n).id
of nkOpenSymChoice, nkClosedSymChoice:
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
uses.incl n[0].sym.name.id
of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
for i in 0..<n.len: computeDeps(cache, n[i], declares, uses, topLevel)
Expand Down
1 change: 1 addition & 0 deletions compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
result.semTypeNode = semTypeNode
result.instTypeBoundOp = sigmatch.instTypeBoundOp
result.hasUnresolvedArgs = hasUnresolvedArgs
result.semAsgnOpr = semAsgnOpr
result.templInstCounter = new int

pushProcCon(result, module)
Expand Down
18 changes: 7 additions & 11 deletions compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
while true:
determineType(c, sym)
z = initCandidate(c, sym, initialBinding, scope, diagnosticsFlag)

# this is kinda backwards as without a check here the described
# problems in recalc would not happen, but instead it 100%
# does check forever in some cases
Expand Down Expand Up @@ -184,7 +183,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# 1) new symbols are discovered but the loop ends before we recalc
# 2) new symbols are discovered and resemmed forever
# not 100% sure if these are possible though as they would rely
# on somehow introducing a new overload during overload resolution
# on somehow introducing a new overload during overload resolution

# Symbol table has been modified. Restart and pre-calculate all syms
# before any further candidate init and compare. SLOW, but rare case.
Expand Down Expand Up @@ -245,14 +244,6 @@ proc effectProblem(f, a: PType; result: var string; c: PContext) =
if not c.graph.compatibleProps(c.graph, f, a):
result.add "\n The `.requires` or `.ensures` properties are incompatible."

proc renderNotLValue(n: PNode): string =
result = $n
let n = if n.kind == nkHiddenDeref: n[0] else: n
if n.kind == nkHiddenCallConv and n.len > 1:
result = $n[0] & "(" & result & ")"
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"

proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
(TPreferedDesc, string) =
var prefer = preferName
Expand Down Expand Up @@ -695,7 +686,12 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
let s = a[0].sym
if s.isGenericRoutineStrict:
let finalCallee = generateInstance(c, s, x.bindings, a.info)
var src = s.typ.firstParamType
var convMatch = newCandidate(c, src)
let srca = typeRel(convMatch, src, a[1].typ)
if srca notin {isEqual, isGeneric, isSubtype}:
internalError(c.config, a.info, "generic converter failed rematch")
let finalCallee = generateInstance(c, s, convMatch.bindings, a.info)
a[0].sym = finalCallee
a[0].typ() = finalCallee.typ
#a.typ = finalCallee.typ.returnType
Expand Down
171 changes: 169 additions & 2 deletions compiler/semdata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@

## This module contains the data structures for the semantic checking phase.

import std/[tables, intsets, sets]
import std/[tables, intsets, sets, strutils]

when defined(nimPreviewSlimSystem):
import std/assertions

import
options, ast, msgs, idents, renderer,
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable,
types, lowerings, trees, parampatterns

import ic / ic

Expand Down Expand Up @@ -173,6 +174,9 @@ type
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
inTypeofContext*: int

semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}

TBorrowState* = enum
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch

Expand Down Expand Up @@ -635,3 +639,166 @@ proc rememberExpansion*(c: PContext; info: TLineInfo; expandedSym: PSym) =
## delegated to the "rod" file mechanism.
if c.config.symbolFiles != disabledSf:
storeExpansion(c.encoder, c.packedRepr, info, expandedSym)

const
errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
errXStackEscape = "address of '$1' may not escape its stack frame"

proc renderNotLValue*(n: PNode): string =
result = $n
let n = if n.kind == nkHiddenDeref: n[0] else: n
if n.kind == nkHiddenCallConv and n.len > 1:
result = $n[0] & "(" & result & ")"
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"

proc isAssignable(c: PContext, n: PNode): TAssignableResult =
result = parampatterns.isAssignable(c.p.owner, n)

proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or
sfCompileToCpp in c.module.flags):
checkSonsLen(n, 1, c.config)
result = n[0]
else:
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
result.add n
let aa = isAssignable(c, n)
let sym = getRoot(n)
if aa notin {arLValue, arLocalLValue}:
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
elif strictDefs in c.features and aa == arAddressableConst and
sym != nil and sym.kind == skLet and isOutParam:
discard "allow let varaibles to be passed to out parameters"
else:
localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))

proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
result = n
case n.kind
of nkSym:
# n.sym.typ can be nil in 'check' mode ...
if n.sym.typ != nil and
skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n.sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
of nkDotExpr:
checkSonsLen(n, 2, c.config)
if n[1].kind != nkSym:
internalError(c.config, n.info, "analyseIfAddressTaken")
return
if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n[1].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
of nkBracketExpr:
checkMinSonsLen(n, 1, c.config)
if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
else:
result = newHiddenAddrTaken(c, n, isOutParam)

proc analyseIfAddressTakenInCall*(c: PContext, n: PNode, isConverter = false) =
checkMinSonsLen(n, 1, c.config)
if n[0].typ == nil:
# n[0] might be erroring node in nimsuggest
return
const
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, mWasMoved}

template checkIfConverterCalled(c: PContext, n: PNode) =
## Checks if there is a converter call which wouldn't be checked otherwise
# Call can sometimes be wrapped in a deref
let node = if n.kind == nkHiddenDeref: n[0] else: n
if node.kind == nkHiddenCallConv:
analyseIfAddressTakenInCall(c, node, true)
# get the real type of the callee
# it may be a proc var with a generic alias type, so we skip over them
var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams:
# BUGFIX: check for L-Value still needs to be done for the arguments!
# note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
for i in 1..<n.len:
if i < t.len and t[i] != nil and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
let it = n[i]
let aa = isAssignable(c, it)
if aa notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr:
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
else:
localError(c.config, it.info, errVarForOutParamNeededX % $it)
# Make sure to still check arguments for converters
c.checkIfConverterCalled(n[i])
# bug #5113: disallow newSeq(result) where result is a 'var T':
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
var arg = n[1] #.skipAddr
if arg.kind == nkHiddenDeref: arg = arg[0]
if arg.kind == nkSym and arg.sym.kind == skResult and
arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments}))

return
for i in 1..<n.len:
let n = if n.kind == nkHiddenDeref: n[0] else: n
c.checkIfConverterCalled(n[i])
if i < t.len and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
# Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet.
# So we need to make sure we are checking them still when in a converter call
if n[i].kind != nkHiddenAddr or isConverter:
n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc})))


proc replaceHookMagic*(c: PContext, n: PNode, kind: TTypeAttachedOp): PNode =
## Replaces builtin generic hooks with lifted hooks.
case kind
of attachedDestructor:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedDestructor)
if op != nil:
result[0] = newSymNode(op)
if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
if n[1].kind == nkSym and n[1].sym.kind == skParam and
n[1].typ.kind == tyVar:
result[1] = genDeref(n[1])
else:
result[1] = skipAddr(n[1])
of attachedTrace:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedTrace)
if op != nil:
result[0] = newSymNode(op)
of attachedDup:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedDup)
if op != nil:
result[0] = newSymNode(op)
if op.typ.len == 3:
let boolLit = newIntLit(c.graph, n.info, 1)
boolLit.typ() = getSysType(c.graph, n.info, tyBool)
result.add boolLit
of attachedWasMoved:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedWasMoved)
if op != nil:
result[0] = newSymNode(op)
analyseIfAddressTakenInCall(c, result, false)
of attachedSink:
result = c.semAsgnOpr(c, n, nkSinkAsgn)
of attachedAsgn:
result = c.semAsgnOpr(c, n, nkAsgn)
of attachedDeepCopy:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, kind)
if op != nil:
result[0] = newSymNode(op)
206 changes: 87 additions & 119 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ when defined(nimCompilerStacktraceHints):
const
errExprXHasNoType = "expression '$1' has no type (or is ambiguous)"
errXExpectsTypeOrValue = "'$1' expects a type or value"
errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
errXStackEscape = "address of '$1' may not escape its stack frame"
errExprHasNoAddress = "expression has no address"
errCannotInterpretNodeX = "cannot evaluate '$1'"
Expand Down Expand Up @@ -725,7 +724,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 @@ -774,7 +773,11 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp

let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
var typ: PType
if constructType:
var isGeneric = false
if yy.typ != nil and yy.typ.kind == tyFromExpr:
isGeneric = true
typ = nil # will not be used
elif constructType:
typ = yy.typ
if expectedElementType == nil:
expectedElementType = typ
Expand All @@ -799,11 +802,21 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp

let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
result.add xx
if constructType:
if xx.typ != nil and xx.typ.kind == tyFromExpr:
isGeneric = true
elif constructType:
typ = commonType(c, typ, xx.typ)
#n[i] = semExprWithType(c, x, {})
#result.add fitNode(c, typ, n[i])
inc(lastIndex)
if isGeneric:
for i in 0..<result.len:
if isIntLit(result[i].typ):
# generic instantiation strips int lit type which makes conversions fail
result[i].typ() = nil
result.typ() = nil # current result.typ is invalid, index type is nil
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
if constructType:
addSonSkipIntLit(result.typ, typ, c.idgen)
for i in 0..<result.len:
Expand Down Expand Up @@ -865,105 +878,6 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
if hasUnresolvedArgs(c, n[i]): return true
return false

proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or
sfCompileToCpp in c.module.flags):
checkSonsLen(n, 1, c.config)
result = n[0]
else:
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
result.add n
let aa = isAssignable(c, n)
let sym = getRoot(n)
if aa notin {arLValue, arLocalLValue}:
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
elif strictDefs in c.features and aa == arAddressableConst and
sym != nil and sym.kind == skLet and isOutParam:
discard "allow let varaibles to be passed to out parameters"
else:
localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))

proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
result = n
case n.kind
of nkSym:
# n.sym.typ can be nil in 'check' mode ...
if n.sym.typ != nil and
skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n.sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
of nkDotExpr:
checkSonsLen(n, 2, c.config)
if n[1].kind != nkSym:
internalError(c.config, n.info, "analyseIfAddressTaken")
return
if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n[1].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
of nkBracketExpr:
checkMinSonsLen(n, 1, c.config)
if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
else:
result = newHiddenAddrTaken(c, n, isOutParam)

proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) =
checkMinSonsLen(n, 1, c.config)
if n[0].typ == nil:
# n[0] might be erroring node in nimsuggest
return
const
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove,
mWasMoved}

template checkIfConverterCalled(c: PContext, n: PNode) =
## Checks if there is a converter call which wouldn't be checked otherwise
# Call can sometimes be wrapped in a deref
let node = if n.kind == nkHiddenDeref: n[0] else: n
if node.kind == nkHiddenCallConv:
analyseIfAddressTakenInCall(c, node, true)
# get the real type of the callee
# it may be a proc var with a generic alias type, so we skip over them
var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams:
# BUGFIX: check for L-Value still needs to be done for the arguments!
# note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
for i in 1..<n.len:
if i < t.len and t[i] != nil and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
let it = n[i]
let aa = isAssignable(c, it)
if aa notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr:
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
else:
localError(c.config, it.info, errVarForOutParamNeededX % $it)
# Make sure to still check arguments for converters
c.checkIfConverterCalled(n[i])
# bug #5113: disallow newSeq(result) where result is a 'var T':
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
var arg = n[1] #.skipAddr
if arg.kind == nkHiddenDeref: arg = arg[0]
if arg.kind == nkSym and arg.sym.kind == skResult and
arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments}))

return
for i in 1..<n.len:
let n = if n.kind == nkHiddenDeref: n[0] else: n
c.checkIfConverterCalled(n[i])
if i < t.len and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
# Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet.
# So we need to make sure we are checking them still when in a converter call
if n[i].kind != nkHiddenAddr or isConverter:
n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc})))

include semmagic

proc evalAtCompileTime(c: PContext, n: PNode): PNode =
Expand Down Expand Up @@ -1873,7 +1787,7 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
result = newTreeI(nkCall, n.info, setterId, a[0], n[1])
result.flags.incl nfDotSetter
let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1])
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
result = semOverloadedCallAnalyseEffects(c, result, orig, {efNoUndeclared})

if result != nil:
result = afterCallActions(c, result, nOrig, {})
Expand Down Expand Up @@ -2267,10 +2181,8 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
result = someSym(c.graph, m, ident)
of nkSym:
result = n.sym
of nkOpenSymChoice, nkClosedSymChoice:
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = n[0].sym
of nkOpenSym:
result = lookUpForDeclared(c, n[0], onlyCurrentScope)
else:
localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
result = nil
Expand Down Expand Up @@ -2808,26 +2720,33 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
else:
# only semantic checking for all elements, later type checking:
var typ: PType = nil
var isGeneric = false
for i in 0..<n.len:
let doSetType = typ == nil
if 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)
if doSetType:
typ = skipTypes(n[i][1].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
n[i].typ() = n[i][2].typ # range node needs type too
if (n[i][1].typ != nil and n[i][1].typ.kind == tyFromExpr) or
(n[i][2].typ != nil and n[i][2].typ.kind == tyFromExpr):
isGeneric = true
else:
if doSetType:
typ = skipTypes(n[i][1].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
n[i].typ() = n[i][2].typ # range node needs type too
elif n[i].kind == nkRange:
# already semchecked
if doSetType:
typ = skipTypes(n[i][0].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
else:
n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
if doSetType:
if n[i].typ != nil and n[i].typ.kind == tyFromExpr:
isGeneric = true
elif doSetType:
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
if doSetType:
if doSetType and not isGeneric:
if not isOrdinalType(typ, allowEnumWithHoles=true):
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
Expand All @@ -2842,6 +2761,14 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
if expectedElementType == nil:
expectedElementType = typ
if isGeneric:
for i in 0..<n.len:
if isIntLit(n[i].typ):
# generic instantiation strips int lit type which makes conversions fail
n[i].typ() = nil
result.add n[i]
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
addSonSkipIntLit(result.typ, typ, c.idgen)
for i in 0..<n.len:
var m: PNode
Expand Down Expand Up @@ -2914,6 +2841,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
var typ = newTypeS(tyTuple, c)
typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs
var ids = initIntSet()
var isGeneric = false
for i in 0..<n.len:
if n[i].kind != nkExprColonExpr:
illFormedAst(n[i], c.config)
Expand All @@ -2923,7 +2851,9 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
# can check if field name matches expected type here
let expectedElemType = if expected != nil: expected[i] else: nil
n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType)
if expectedElemType != nil and
if n[i][1].typ != nil and n[i][1].typ.kind == tyFromExpr:
isGeneric = true
elif 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})]`,
Expand All @@ -2944,7 +2874,21 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
typ.n.add newSymNode(f)
n[i][0] = newSymNode(f)
result.add n[i]
if isGeneric:
for i in 0..<result.len:
if isIntLit(result[i][1].typ):
# generic instantiation strips int lit type which makes conversions fail
result[i][1].typ() = nil
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
let oldType = n.typ
result.typ() = typ
if oldType != nil and not hasEmpty(oldType): # see hasEmpty comment above
# convert back to old type
let conversion = indexTypesMatch(c, oldType, typ, result)
# ignore matching error, the goal is just to keep the original type info
if conversion != nil:
result.typ() = oldType

proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = n # we don't modify n, but compute the type:
Expand All @@ -2955,17 +2899,37 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT
if not (expected.kind == tyTuple and expected.len == n.len):
expected = nil
var typ = newTypeS(tyTuple, c) # leave typ.n nil!
var isGeneric = false
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
if n[i].typ != nil and n[i].typ.kind == tyFromExpr:
isGeneric = true
elif 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)
let conversion = indexTypesMatch(c, expectedElemType, n[i].typ, n[i])
# ignore matching error, full tuple will be matched later which may call converter, see #24609
if conversion != nil:
n[i] = conversion
addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
if isGeneric:
for i in 0..<result.len:
if isIntLit(result[i].typ):
# generic instantiation strips int lit type which makes conversions fail
result[i].typ() = nil
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
let oldType = n.typ
result.typ() = typ
if oldType != nil and not hasEmpty(oldType): # see hasEmpty comment above
# convert back to old type
let conversion = indexTypesMatch(c, oldType, typ, result)
# ignore matching error, the goal is just to keep the original type info
if conversion != nil:
result.typ() = oldType

include semobjconstr

Expand Down Expand Up @@ -3054,9 +3018,15 @@ proc semExport(c: PContext, n: PNode): PNode =
s = nextOverloadIter(o, c, a)

proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
var tupexp = semTuplePositionsConstr(c, n, flags, expectedType)
result = semTuplePositionsConstr(c, n, flags, expectedType)
if result.typ.kind == tyFromExpr:
# tyFromExpr is already ambivalent between types and values
return
var tupexp = result
while tupexp.kind == nkHiddenSubConv: tupexp = tupexp[1]
var isTupleType: bool = false
if tupexp.len > 0: # don't interpret () as type
internalAssert c.config, tupexp.kind == nkTupleConstr
isTupleType = tupexp[0].typ.kind == tyTypeDesc
# check if either everything or nothing is tyTypeDesc
for i in 1..<tupexp.len:
Expand All @@ -3066,8 +3036,6 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
result = n
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
result.typ() = makeTypeDesc(c, typ)
else:
result = tupexp

proc isExplicitGenericCall(c: PContext, n: PNode): bool =
## checks if a call node `n` is a routine call with explicit generic params
Expand Down
6 changes: 4 additions & 2 deletions compiler/semfields.nim
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
of nkIdent, nkSym:
result = n
let ident = considerQuotedIdent(c.c, n)
if c.replaceByFieldName:
if c.replaceByFieldName and
ident.id != ord(wUnderscore):
if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
let fieldName = if c.tupleType.isNil: c.field.name.s
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
Expand All @@ -45,7 +46,8 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
return
# other fields:
for i in ord(c.replaceByFieldName)..<forLoop.len-2:
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id and
ident.id != ord(wUnderscore):
var call = forLoop[^2]
var tupl = call[i+1-ord(c.replaceByFieldName)]
if c.field.isNil:
Expand Down
7 changes: 4 additions & 3 deletions compiler/semfold.nim
Original file line number Diff line number Diff line change
Expand Up @@ -471,19 +471,20 @@ proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNo
if result.kind == nkExprColonExpr: result = result[1]
else:
result = nil
localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
#localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
of nkBracket:
idx -= toInt64(firstOrd(g.config, x.typ))
if idx >= 0 and idx < x.len: result = x[int(idx)]
else:
result = nil
localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
#localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
of nkStrLit..nkTripleStrLit:
result = newNodeIT(nkCharLit, x.info, n.typ)
if idx >= 0 and idx < x.strVal.len:
result.intVal = ord(x.strVal[int(idx)])
else:
localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
result = nil
#localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
else: result = nil

proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
Expand Down
4 changes: 4 additions & 0 deletions compiler/seminst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
param.typ = result[i]

result.n[i] = newSymNode(param)
if isRecursiveStructuralType(result[i]):
localError(c.config, originalParams[i].sym.info, "illegal recursion in type '" & typeToString(result[i]) & "'")
propagateToOwner(result, result[i])
addDecl(c, param)

Expand All @@ -318,6 +320,8 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
cl.isReturnType = false
result.n[0] = originalParams[0].copyTree
if result[0] != nil:
if isRecursiveStructuralType(result[0]):
localError(c.config, originalParams[0].info, "illegal recursion in type '" & typeToString(result[0]) & "'")
propagateToOwner(result, result[0])

eraseVoidParams(result)
Expand Down
7 changes: 6 additions & 1 deletion compiler/semmacrosanity.nim
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,12 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
of nkCurly:
if x.kind in {tySet}:
n.typ() = t
for m in n: annotateType(m, x.elemType, conf)
for m in n:
if m.kind == nkRange:
annotateType(m[0], x.elemType, conf)
annotateType(m[1], x.elemType, conf)
else:
annotateType(m, x.elemType, conf)
else:
globalError(conf, n.info, "{} must have the set type")
of nkFloatLit..nkFloat128Lit:
Expand Down
52 changes: 16 additions & 36 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,15 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
result.add typExpr
if typExpr.typ.kind == tyFromExpr:
typExpr.typ.flags.incl tfNonConstExpr
result.typ() = makeTypeDesc(c, typExpr.typ)
var t = typExpr.typ
if t.kind == tyStatic:
let base = t.skipTypes({tyStatic})
if c.inGenericContext > 0 and base.containsGenericType:
t = makeTypeFromExpr(c, copyTree(typExpr))
t.flags.incl tfNonConstExpr
else:
t = base
result.typ() = makeTypeDesc(c, t)

type
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
Expand Down Expand Up @@ -604,9 +612,10 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mArrPut:
result = semArrPut(c, n, flags)
of mAsgn:
if n[0].sym.name.s == "=":
case n[0].sym.name.s
of "=", "=copy":
result = semAsgnOpr(c, n, nkAsgn)
elif n[0].sym.name.s == "=sink":
of "=sink":
result = semAsgnOpr(c, n, nkSinkAsgn)
else:
result = semShallowCopy(c, n, flags)
Expand Down Expand Up @@ -645,42 +654,13 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mNewFinalize:
result = semNewFinalize(c, n)
of mDestroy:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedDestructor)
if op != nil:
result[0] = newSymNode(op)
if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
if n[1].kind == nkSym and n[1].sym.kind == skParam and
n[1].typ.kind == tyVar:
result[1] = genDeref(n[1])
else:
result[1] = skipAddr(n[1])
result = replaceHookMagic(c, n, attachedDestructor)
of mTrace:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedTrace)
if op != nil:
result[0] = newSymNode(op)
result = replaceHookMagic(c, n, attachedTrace)
of mDup:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedDup)
if op != nil:
result[0] = newSymNode(op)
if op.typ.len == 3:
let boolLit = newIntLit(c.graph, n.info, 1)
boolLit.typ() = getSysType(c.graph, n.info, tyBool)
result.add boolLit
result = replaceHookMagic(c, n, attachedDup)
of mWasMoved:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedWasMoved)
if op != nil:
result[0] = newSymNode(op)
let addrExp = newNodeIT(nkHiddenAddr, result[1].info, makePtrType(c, t))
addrExp.add result[1]
result[1] = addrExp
result = replaceHookMagic(c, n, attachedWasMoved)
of mUnown:
result = semUnown(c, n)
of mExists, mForall:
Expand Down
43 changes: 33 additions & 10 deletions compiler/sempass2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import
ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, options, guards, lineinfos, semfold, semdata,
modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
semstrictfuncs, suggestsymdb, pushpoppragmas
semstrictfuncs, suggestsymdb, pushpoppragmas, lowerings

import std/[tables, intsets, strutils, sequtils]

Expand Down Expand Up @@ -84,6 +84,7 @@ type
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
isInnerProc: bool
inEnforcedNoSideEffects: bool
unknownRaises: seq[(PSym, TLineInfo)]
currOptions: TOptions
optionsStack: seq[(TOptions, TNoteKinds)]
config: ConfigRef
Expand Down Expand Up @@ -638,6 +639,8 @@ proc importedFromC(n: PNode): bool =
proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
let pragma = s.ast[pragmasPos]
let spec = effectSpec(pragma, wRaises)
if spec.isNil and sfForward in s.flags:
tracked.unknownRaises.add (s, n.info)
mergeRaises(tracked, spec, n)

let tagSpec = effectSpec(pragma, wTags)
Expand Down Expand Up @@ -977,6 +980,25 @@ proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s:
if optIdeExceptionInlayHints in tracked.config.globalOptions:
internalMarkCaughtExceptions(tracked, g.suggestSymbols.mgetOrPut(info.fileIndex, newSuggestFileSymbolDatabase(info.fileIndex, true)), info)

proc findHookKind(name: string): (bool, TTypeAttachedOp) =
case name.normalize
of "=wasmoved":
result = (true, attachedWasMoved)
of "=destroy":
result = (true, attachedDestructor)
of "=copy", "=":
result = (true, attachedAsgn)
of "=dup":
result = (true, attachedDup)
of "=sink":
result = (true, attachedSink)
of "=trace":
result = (true, attachedTrace)
of "=deepcopy":
result = (true, attachedDeepCopy)
else:
result = (false, attachedWasMoved)

proc trackCall(tracked: PEffects; n: PNode) =
template gcsafeAndSideeffectCheck() =
if notGcSafe(op) and not importedFromC(a):
Expand Down Expand Up @@ -1065,18 +1087,17 @@ proc trackCall(tracked: PEffects; n: PNode) =
checkBounds(tracked, n[1], n[2])


var n = n
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
tracked.owner.kind != skMacro:
var opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
if a.sym.name.s == "=": opKind = attachedAsgn.int
if opKind != -1:
var (isHook, opKind) = findHookKind(a.sym.name.s)
if isHook:
# rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
if a.sym != getAttachedOp(tracked.graph, t, opKind):
createTypeBoundOps(tracked, t, n.info, explicit = true)
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
if op != nil:
n[0].sym = op
# replace builtin hooks with lifted ones
n = replaceHookMagic(tracked.c, n, opKind)

if op != nil and op.kind == tyProc:
for i in 1..<min(n.safeLen, op.signatureLen):
Expand Down Expand Up @@ -1505,7 +1526,7 @@ proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =

proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg: string, hints: bool;
effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.};
hintsArg: PNode = nil; isForbids: bool = false) =
hintsArg: PNode = nil; isForbids: bool = false; unknownRaises: seq[(PSym, TLineInfo)] = @[]) =
# check that any real exception is listed in 'spec'; mark those as used;
# report any unused exception
var used = initIntSet()
Expand All @@ -1522,6 +1543,8 @@ proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg:
pushInfoContext(g.config, spec.info)
var rr = if r.kind == nkRaiseStmt: r[0] else: r
while rr.kind in {nkStmtList, nkStmtListExpr} and rr.len > 0: rr = rr.lastSon
for (s, info) in unknownRaises.items:
message(g.config, info, hintUnknownRaises, s.name.s)
message(g.config, r.info, if emitWarnings: warnEffect else: errGenerated,
renderTree(rr) & " " & msg & typeToString(r.typ))
popInfoContext(g.config)
Expand Down Expand Up @@ -1679,7 +1702,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
if not isNil(raisesSpec):
let useWarning = s.name.s == "=destroy"
checkRaisesSpec(g, useWarning, raisesSpec, t.exc, "can raise an unlisted exception: ",
hints=on, subtypeRelation, hintsArg=s.ast[0])
hints=on, subtypeRelation, hintsArg=s.ast[0], unknownRaises = t.unknownRaises)
# after the check, use the formal spec:
effects[exceptionEffects] = raisesSpec
else:
Expand Down
3 changes: 0 additions & 3 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2158,9 +2158,6 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
if op == attachedDestructor and t.firstParamType.kind == tyVar and
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter")
obj = canonType(c, obj)
let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
Expand Down
60 changes: 41 additions & 19 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
styleCheckDef(c, a[j].info, field)
onDef(field.info, field)
if result.n.len == 0: result.n = nil
if isTupleRecursive(result):
if isRecursiveStructuralType(result):
localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(result))

proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
Expand Down Expand Up @@ -1289,12 +1289,14 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType[i] = lifted
result = paramType
result.last.shouldHaveMeta

let liftBody = recurse(paramType.skipModifier, true)
if liftBody != nil:
result = liftBody
result.flags.incl tfHasMeta
#result.shouldHaveMeta
if paramType.isConcept:
return addImplicitGeneric(c, paramType, paramTypId, info, genericParams, paramName)
else:
let liftBody = recurse(paramType.skipModifier, true)
if liftBody != nil:
result = liftBody
result.flags.incl tfHasMeta
#result.shouldHaveMeta

of tyGenericInvocation:
result = nil
Expand All @@ -1308,7 +1310,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
# this may happen for proc type appearing in a type section
# before one of its param types
return

if body.last.kind == tyUserTypeClass:
let expanded = instGenericContainer(c, info, paramType,
allowMetaTypes = true)
Expand Down Expand Up @@ -1465,6 +1466,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if isType: localError(c.config, a.info, "':' expected")
if kind in {skTemplate, skMacro}:
typ = newTypeS(tyUntyped, c)
elif isRecursiveStructuralType(typ):
localError(c.config, a[^2].info, errIllegalRecursionInTypeX % typeToString(typ))
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
continue

Expand Down Expand Up @@ -1501,7 +1504,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
addParamOrResult(c, arg, kind)
styleCheckDef(c, a[j].info, arg)
onDef(a[j].info, arg)
a[j] = newSymNode(arg)
if a[j].kind == nkPragmaExpr:
a[j][0] = newSymNode(arg)
else:
a[j] = newSymNode(arg)

var r: PType = nil
if n[0].kind != nkEmpty:
Expand All @@ -1525,7 +1531,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if r != nil:
# turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
if isRecursiveStructuralType(r):
localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(r))
elif skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
if kind notin {skMacro, skTemplate} and r.kind in {tyTyped, tyUntyped}:
localError(c.config, n[0].info, "return type '" & typeToString(r) &
"' is only valid for macros and templates")
Expand Down Expand Up @@ -1712,7 +1720,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
# special check for generic object with
# generic/partial specialized parent
let tx = result.skipTypes(abstractPtrs, 50)
if tx.isNil or isTupleRecursive(tx):
if tx.isNil or isRecursiveStructuralType(tx):
localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0]))
return errorType(c)
if tx != result and tx.kind == tyObject:
Expand All @@ -1733,10 +1741,10 @@ proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
else:
result = nil

proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
proc fixupTypeOf(c: PContext, prev: PType, typ: PType) =
if prev != nil:
let result = newTypeS(tyAlias, c)
result.rawAddSon typExpr.typ
result.rawAddSon typ
result.sym = prev.sym
if prev.kind != tyGenericBody:
assignType(prev, result)
Expand Down Expand Up @@ -1928,12 +1936,19 @@ proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let t = semExprWithType(c, n, {efInTypeof})
let ex = semExprWithType(c, n, {efInTypeof})
closeScope(c)
fixupTypeOf(c, prev, t)
result = t.typ
result = ex.typ
if result.kind == tyFromExpr:
result.flags.incl tfNonConstExpr
elif result.kind == tyStatic:
let base = result.skipTypes({tyStatic})
if c.inGenericContext > 0 and base.containsGenericType:
result = makeTypeFromExpr(c, copyTree(ex))
result.flags.incl tfNonConstExpr
else:
result = base
fixupTypeOf(c, prev, result)

proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
Expand All @@ -1946,12 +1961,19 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
m = mode.intVal
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
let ex = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
closeScope(c)
fixupTypeOf(c, prev, t)
result = t.typ
result = ex.typ
if result.kind == tyFromExpr:
result.flags.incl tfNonConstExpr
elif result.kind == tyStatic:
let base = result.skipTypes({tyStatic})
if c.inGenericContext > 0 and base.containsGenericType:
result = makeTypeFromExpr(c, copyTree(ex))
result.flags.incl tfNonConstExpr
else:
result = base
fixupTypeOf(c, prev, result)

proc semTypeIdent(c: PContext, n: PNode): PSym =
if n.kind == nkSym:
Expand Down
11 changes: 7 additions & 4 deletions compiler/semtypinst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
if t.kind in tyTypeClasses: discard
elif t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}:
localError(conf, info, "type 'var var' is not allowed")
elif computeSize(conf, t) == szIllegalRecursion or isTupleRecursive(t):
elif computeSize(conf, t) == szIllegalRecursion or isRecursiveStructuralType(t):
localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'")

proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
Expand Down Expand Up @@ -608,10 +608,13 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
result = t
if t == nil: return

var et = t
if t.isConcept:
et = t.reduceToBase
const lookupMetas = {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses - {tyAnything}
if t.kind in lookupMetas or
(t.kind == tyAnything and tfRetType notin t.flags):
let lookup = cl.typeMap.lookup(t)
if et.kind in lookupMetas or
(et.kind == tyAnything and tfRetType notin et.flags):
let lookup = cl.typeMap.lookup(et)
if lookup != nil: return lookup

case t.kind
Expand Down
84 changes: 58 additions & 26 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import std/[intsets, strutils, tables]
when defined(nimPreviewSlimSystem):
import std/assertions


type
MismatchKind* = enum
kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded,
Expand Down Expand Up @@ -94,6 +95,7 @@ type
trNoCovariance
trBindGenericParam # bind tyGenericParam even with trDontBind
trIsOutParam
trCheckGeneric

TTypeRelFlags* = set[TTypeRelFlag]

Expand Down Expand Up @@ -297,9 +299,9 @@ proc checkGeneric(a, b: TCandidate): int =
var winner = 0
for aai, bbi in underspecifiedPairs(aa, bb, 1):
var ma = newCandidate(c, bbi)
let tra = typeRel(ma, bbi, aai, {trDontBind})
let tra = typeRel(ma, bbi, aai, {trDontBind, trCheckGeneric})
var mb = newCandidate(c, aai)
let trb = typeRel(mb, aai, bbi, {trDontBind})
let trb = typeRel(mb, aai, bbi, {trDontBind, trCheckGeneric})
if tra == isGeneric and trb in {isNone, isInferred, isInferredConvertible}:
if winner == -1: return 0
winner = 1
Expand Down Expand Up @@ -363,6 +365,8 @@ proc sumGeneric(t: PType): int =
result += sumGeneric(a)
break
else:
if t.isConcept:
result += t.reduceToBase.conceptBody.len
break

proc complexDisambiguation(a, b: PType): int =
Expand Down Expand Up @@ -1138,6 +1142,24 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
else:
return false

proc enterConceptMatch(c: var TCandidate; f,a: PType, flags: TTypeRelFlags): TTypeRelation =
var
conceptFlags: set[MatchFlags] = {}
container: PType = nil
concpt = f
if concpt.kind != tyConcept:
container = concpt
concpt = container.reduceToBase
if trDontBind in flags:
conceptFlags.incl mfDontBind
if trCheckGeneric in flags:
conceptFlags.incl mfCheckGeneric
let mres = concepts.conceptMatch(c.c, concpt, a, c.bindings, container, flags = conceptFlags)
if mres:
isGeneric
else:
isNone

when false:
proc maxNumericType(prev, candidate: PType): PType =
let c = candidate.skipTypes({tyRange})
Expand Down Expand Up @@ -1212,9 +1234,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
else:
var candidate = f

case f.kind
let fType = f.skipTypes({tySink})
case fType.kind
of tyGenericParam:
var prev = lookup(c.bindings, f)
var prev = lookup(c.bindings, fType)
if prev != nil: candidate = prev
of tyFromExpr:
let computedType = tryResolvingStaticExpr(c, f.n).typ
Expand Down Expand Up @@ -1424,7 +1447,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
return isNone

if fRange.rangeHasUnresolvedStatic:
if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange:
if (aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange) or
(aRange.kind == tyRange and aRange.rangeHasUnresolvedStatic):
return
return inferStaticsInRange(c, fRange, a)
elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
Expand Down Expand Up @@ -1517,12 +1541,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
reduceToBase(a)
if effectiveArgType.kind == tyObject:
if sameObjectTypes(f, effectiveArgType):
c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
if tfFinal notin f.flags:
inc c.inheritancePenalty, ord(c.inheritancePenalty < 0)
result = isEqual
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
elif trIsOutParam notin flags:
c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil)
if c.inheritancePenalty > 0:
let depth = isObjectSubtype(c, effectiveArgType, f, nil)
if depth > 0:
inc c.inheritancePenalty, depth + ord(c.inheritancePenalty < 0)
result = isSubtype
of tyDistinct:
a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
Expand Down Expand Up @@ -1642,8 +1668,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,

let roota = if skipBoth or deptha > depthf: a.skipGenericAlias else: a
let rootf = if skipBoth or depthf > deptha: f.skipGenericAlias else: f

if a.kind == tyGenericInst:

if f.isConcept:
result = enterConceptMatch(c, rootf, roota, flags)
elif a.kind == tyGenericInst:
if roota.base == rootf.base:
let nextFlags = flags + {trNoCovariance}
var hasCovariance = false
Expand Down Expand Up @@ -1692,7 +1720,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
if aAsObject.kind == tyObject and trIsOutParam notin flags:
let baseType = aAsObject.base
if baseType != nil:
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
if tfFinal notin aAsObject.flags:
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
let ret = typeRel(c, f, baseType, flags)
return if ret in {isEqual,isGeneric}: isSubtype else: ret

Expand All @@ -1713,7 +1742,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
var x = a.skipGenericAlias
if x.kind == tyGenericParam and x.len > 0:
x = x.last
let concpt = f[0].skipTypes({tyGenericBody})
let concpt = f.reduceToBase
var preventHack = concpt.kind == tyConcept
if x.kind == tyOwned and f[0].kind != tyOwned:
preventHack = true
Expand All @@ -1733,6 +1762,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
let tr = typeRel(c, f[i], x[i], flags)
if tr <= isSubtype: return
result = isGeneric
let impl = last(f[0])
if impl.kind == tyObject and tfFinal notin impl.flags:
# match non-invocation case
inc c.inheritancePenalty, 0 + int(c.inheritancePenalty < 0)
elif x.kind == tyGenericInst and f[0] == x[0] and
x.len - 1 == f.len:
for i in 1..<f.len:
Expand All @@ -1742,17 +1775,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
# Workaround for regression #4589
if f[i].kind != tyTypeDesc: return
result = isGeneric
elif x.kind == tyGenericInst and concpt.kind == tyConcept:
result = if concepts.conceptMatch(c.c, concpt, x, c.bindings, f): isGeneric
else: isNone
elif concpt.kind == tyConcept:
result = enterConceptMatch(c, f, x, flags)
else:
let genericBody = f[0]
var askip = skippedNone
var fskip = skippedNone
let aobj = x.skipToObject(askip)
let fobj = genericBody.last.skipToObject(fskip)
result = typeRel(c, genericBody, x, flags)
if result != isNone:
if result != isNone and concpt.kind != tyConcept:
# see tests/generics/tgeneric3.nim for an example that triggers this
# piece of code:
#
Expand Down Expand Up @@ -1789,7 +1821,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
depth = -1

if depth >= 0:
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
if aobj.kind == tyObject and tfFinal notin aobj.flags:
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
# bug #4863: We still need to bind generic alias crap, so
# we cannot return immediately:
result = if depth == 0: isGeneric else: isSubtype
Expand All @@ -1815,9 +1848,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
if c.inheritancePenalty > -1:
minInheritance = min(minInheritance, c.inheritancePenalty)
result = x
c.inheritancePenalty = oldInheritancePenalty
if result >= isIntConv:
if minInheritance < maxInheritancePenalty:
c.inheritancePenalty = oldInheritancePenalty + minInheritance
inc c.inheritancePenalty, minInheritance + ord(c.inheritancePenalty < 0)
if result > isGeneric: result = isGeneric
bindingRet result
else:
Expand Down Expand Up @@ -1879,11 +1913,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
else:
result = isNone
of tyConcept:
if a.kind == tyConcept and sameType(f, a):
result = isGeneric
else:
result = if concepts.conceptMatch(c.c, f, a, c.bindings, nil): isGeneric
else: isNone
result = enterConceptMatch(c, f, a, flags)
of tyCompositeTypeClass:
considerPreviousT:
let roota = a.skipGenericAlias
Expand Down Expand Up @@ -2155,6 +2185,8 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
# keep varness
if arg.typ != nil and arg.typ.kind == tyVar:
result.typ() = toVar(result.typ, tyVar, c.idgen)
# copy the tfVarIsPtr flag
result.typ.flags = arg.typ.flags
else:
result.typ() = result.typ.skipTypes({tyVar})

Expand Down Expand Up @@ -2264,7 +2296,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
# for generic type converters we need to check 'src <- a' before
# 'f <- dest' in order to not break the unification:
# see tests/tgenericconverter:
let srca = typeRel(m, src, a)
var convMatch = newCandidate(c, src)
let srca = typeRel(convMatch, src, a)
if srca notin {isEqual, isGeneric, isSubtype}: continue

# What's done below matches the logic in ``matchesAux``
Expand All @@ -2276,7 +2309,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,

let destIsGeneric = containsGenericType(dest)
if destIsGeneric:
dest = generateTypeInstance(c, m.bindings, arg, dest)
dest = generateTypeInstance(c, convMatch.bindings, arg, dest)
let fdest = typeRel(m, f, dest)
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
# can't fully mark used yet, may not be used in final call
Expand Down Expand Up @@ -2404,7 +2437,6 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,

let oldInheritancePenalty = m.inheritancePenalty
var r = typeRel(m, f, a)

# This special typing rule for macros and templates is not documented
# anywhere and breaks symmetry. It's hard to get rid of though, my
# custom seqs example fails to compile without this:
Expand Down
20 changes: 20 additions & 0 deletions compiler/transf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,23 @@ proc transformCall(c: PTransf, n: PNode): PNode =
else:
result = s

proc transformBareExcept(c: PTransf, n: PNode): PNode =
result = newTransNode(nkExceptBranch, n, 1)
if isEmptyType(n[0].typ):
result[0] = newNodeI(nkStmtList, n[0].info)
else:
result[0] = newNodeIT(nkStmtListExpr, n[0].info, n[0].typ)
# Generating `raiseDefect()`
let raiseDefectCall = callCodegenProc(c.graph, "raiseDefect", n[0].info)
result[0].add raiseDefectCall
if n[0].kind in {nkStmtList, nkStmtListExpr}:
# flattens stmtList
for son in n[0]:
result[0].add son
else:
result[0].add n[0]
result[0] = transform(c, result[0])

proc transformExceptBranch(c: PTransf, n: PNode): PNode =
if n[0].isInfixAs() and not isImportedException(n[0][1].typ, c.graph.config):
let excTypeNode = n[0][1]
Expand Down Expand Up @@ -985,6 +1002,9 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode =
# Replace the `Exception as foobar` with just `Exception`.
result[0] = transform(c, n[0][1])
result[1] = actions
elif n.len == 1 and
noPanicOnExcept notin c.graph.config.legacyFeatures:
result = transformBareExcept(c, n)
else:
result = transformSons(c, n)

Expand Down
6 changes: 3 additions & 3 deletions compiler/trees.nim
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ proc isRange*(n: PNode): bool {.inline.} =
let callee = n[0]
if (callee.kind == nkIdent and callee.ident.id == ord(wDotDot)) or
(callee.kind == nkSym and callee.sym.name.id == ord(wDotDot)) or
(callee.kind in {nkClosedSymChoice, nkOpenSymChoice} and
callee[1].sym.name.id == ord(wDotDot)):
(callee.kind in {nkClosedSymChoice, nkOpenSymChoice, nkOpenSym} and
callee[0].sym.name.id == ord(wDotDot)):
result = true
else:
result = false
Expand All @@ -145,7 +145,7 @@ proc whichPragma*(n: PNode): TSpecialWord =
of nkIdent: result = whichKeyword(key.ident)
of nkSym: result = whichKeyword(key.sym.name)
of nkCast: return wCast
of nkClosedSymChoice, nkOpenSymChoice:
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym:
return whichPragma(key[0])
of nkBracketExpr:
if n.kind notin nkPragmaCallKinds: return wInvalid
Expand Down
64 changes: 56 additions & 8 deletions compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1420,7 +1420,7 @@ proc sameBackendTypeIgnoreRange*(x, y: PType): bool =

proc sameBackendTypePickyAliases*(x, y: PType): bool =
var c = initSameTypeClosure()
c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases}
c.flags.incl {IgnoreTupleFields, IgnoreRangeShallow, PickyCAliases, PickyBackendAliases}
c.cmp = dcEqIgnoreDistinct
result = sameTypeAux(x, y, c)

Expand Down Expand Up @@ -1485,8 +1485,17 @@ proc commonSuperclass*(a, b: PType): PType =
y = y.baseClass

proc lacksMTypeField*(typ: PType): bool {.inline.} =
## Returns true if the type is an object that lacks a m_type field.
## It doesn't check base classes.
(typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags

proc isObjLackingTypeField*(typ: PType): bool {.inline.} =
## Returns true if the type is an object that lacks a type field.
## Object types that store type headers are not final or pure and
## have inheritable root types, which are not pure, neither.
result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
(typ.baseClass == nil) or isPureObject(typ))

include sizealignoffsetimpl

proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt =
Expand All @@ -1506,6 +1515,31 @@ proc getSize*(conf: ConfigRef; typ: PType): BiggestInt =
computeSizeAlign(conf, typ)
result = typ.size

proc setImportedTypeSize*(conf: ConfigRef, t: PType, size: int) =
t.size = size
if tfPacked in t.flags or size <= 1:
t.align = 1
elif size <= 2:
t.align = 2
elif size <= 4:
t.align = 4
else:
t.align = floatInt64Align(conf)

proc isConcept*(t: PType): bool=
case t.kind
of tyConcept: true
of tyCompositeTypeClass:
t.hasElementType and isConcept(t.elementType)
of tyGenericBody:
t.typeBodyImpl.kind == tyConcept
of tyGenericInvocation, tyGenericInst:
if t.baseClass.kind == tyGenericBody:
t.baseClass.typeBodyImpl.kind == tyConcept
else:
t.baseClass.kind == tyConcept
else: false

proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
case t.kind
of tyStatic:
Expand All @@ -1516,6 +1550,8 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
return false
of GenericTypes + tyTypeClasses + {tyFromExpr}:
return true
of tyGenericInst:
return t.isConcept
else:
return false

Expand Down Expand Up @@ -1861,7 +1897,7 @@ proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: P
processPragmaAndCallConvMismatch(msg, a, b, conf)
localError(conf, info, msg)

proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool =
proc isRecursiveStructuralType(t: PType, cycleDetector: var IntSet): bool =
if t == nil:
return false
if cycleDetector.containsOrIncl(t.id):
Expand All @@ -1872,19 +1908,30 @@ proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool =
var cycleDetectorCopy: IntSet
for a in t.kids:
cycleDetectorCopy = cycleDetector
if isTupleRecursive(a, cycleDetectorCopy):
if isRecursiveStructuralType(a, cycleDetectorCopy):
return true
of tyProc:
result = false
var cycleDetectorCopy: IntSet
if t.returnType != nil:
cycleDetectorCopy = cycleDetector
if isRecursiveStructuralType(t.returnType, cycleDetectorCopy):
return true
for _, a in t.paramTypes:
cycleDetectorCopy = cycleDetector
if isRecursiveStructuralType(a, cycleDetectorCopy):
return true
of tyRef, tyPtr, tyVar, tyLent, tySink,
tyArray, tyUncheckedArray, tySequence, tyDistinct:
return isTupleRecursive(t.elementType, cycleDetector)
return isRecursiveStructuralType(t.elementType, cycleDetector)
of tyAlias, tyGenericInst:
return isTupleRecursive(t.skipModifier, cycleDetector)
return isRecursiveStructuralType(t.skipModifier, cycleDetector)
else:
return false

proc isTupleRecursive*(t: PType): bool =
proc isRecursiveStructuralType*(t: PType): bool =
var cycleDetector = initIntSet()
isTupleRecursive(t, cycleDetector)
isRecursiveStructuralType(t, cycleDetector)

proc isException*(t: PType): bool =
# check if `y` is object type and it inherits from Exception
Expand Down Expand Up @@ -2044,6 +2091,7 @@ proc genericRoot*(t: PType): PType =

proc reduceToBase*(f: PType): PType =
#[
Not recursion safe
Returns the lowest order (most general) type that that is compatible with the input.
E.g.
A[T] = ptr object ... A -> ptr object
Expand All @@ -2068,7 +2116,7 @@ proc reduceToBase*(f: PType): PType =
result = reduceToBase(f.typeBodyImpl)
of tyUserTypeClass:
if f.isResolvedUserTypeClass:
result = f.base # ?? idk if this is right
result = f.base
else:
result = f.skipModifier
of tyStatic, tyOwned, tyVar, tyLent, tySink:
Expand Down
9 changes: 6 additions & 3 deletions compiler/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import semmacrosanity
import
std/[strutils, tables, parseutils],
std/[strutils, tables, intsets, parseutils],
msgs, vmdef, vmgen, nimsets, types,
parser, vmdeps, idents, trees, renderer, options, transf,
gorgeimpl, lineinfos, btrees, macrocacheimpl,
Expand Down Expand Up @@ -2051,7 +2051,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
aStrVal = aNode.ident.s.cstring
of nkSym:
aStrVal = aNode.sym.name.s.cstring
of nkOpenSymChoice, nkClosedSymChoice:
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
aStrVal = aNode[0].sym.name.s.cstring
else:
discard
Expand All @@ -2063,7 +2063,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
bStrVal = bNode.ident.s.cstring
of nkSym:
bStrVal = bNode.sym.name.s.cstring
of nkOpenSymChoice, nkClosedSymChoice:
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
bStrVal = bNode[0].sym.name.s.cstring
else:
discard
Expand Down Expand Up @@ -2425,9 +2425,12 @@ proc evalConstExprAux(module: PSym; idgen: IdGenerator;
setupGlobalCtx(module, g, idgen)
var c = PCtx g.vm
let oldMode = c.mode
let oldLocals = c.locals
c.mode = mode
c.locals = initIntSet()
c.cannotEval = false
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
c.locals = oldLocals
if c.cannotEval:
return errorNode(idgen, prc, n)
if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
Expand Down
3 changes: 2 additions & 1 deletion compiler/vmdef.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
## This module contains the type definitions for the new evaluation engine.
## An instruction is 1-3 int32s in memory, it is a register based VM.

import std/[tables, strutils]
import std/[tables, strutils, intsets]

import ast, idents, options, modulegraphs, lineinfos

Expand Down Expand Up @@ -272,6 +272,7 @@ type
vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC)
procToCodePos*: Table[int, int]
cannotEval*: bool
locals*: IntSet

PStackFrame* = ref TStackFrame
TStackFrame* {.acyclic.} = object
Expand Down
15 changes: 11 additions & 4 deletions compiler/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,7 @@ proc genBindSym(c: PCtx; n: PNode; dest: var TDest) =
# if dynamicBindSym notin c.config.features:
if n.len == 2: # hmm, reliable?
# bindSym with static input
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkOpenSym, nkSym}:
let idx = c.genLiteral(n[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, opcNBindSym, dest, idx)
Expand Down Expand Up @@ -1583,6 +1583,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
# are in the right scope:
if sfGenSym in s.flags and c.prc.sym == nil: discard
elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard
elif s.kind in {skVar, skLet} and s.id in c.locals: discard
else: cannotEval(c, n)
elif s.kind in {skProc, skFunc, skConverter, skMethod,
skIterator} and sfForward in s.flags:
Expand Down Expand Up @@ -1884,6 +1885,10 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
c.freeTemp(objR)

proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
if n[0].typ == nil:
globalError(c.config, n.info, "cannot access array with nil type")
return

let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
case arrayType
of tyString, tyCstring:
Expand Down Expand Up @@ -1975,7 +1980,7 @@ proc genVarSection(c: PCtx; n: PNode) =
c.gen(lowerTupleUnpacking(c.graph, a, c.idgen, c.getOwner))
elif a[0].kind == nkSym:
let s = a[0].sym
checkCanEval(c, a[0])
c.locals.incl(s.id)
if s.isGlobal:
let runtimeAccessToCompileTime = c.mode == emRepl and
sfCompileTime in s.flags and s.position > 0
Expand Down Expand Up @@ -2312,9 +2317,11 @@ proc genStmt*(c: PCtx; n: PNode): int =
result = c.code.len
var d: TDest = -1
c.gen(n, d)
c.gABC(n, opcEof)
if d >= 0:
globalError(c.config, n.info, "VM problem: dest register is set")
# for discardable calls etc, otherwise not valid
freeTemp(c, d)
#globalError(c.config, n.info, "VM problem: dest register is set")
c.gABC(n, opcEof)

proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int =
c.removeLastEof
Expand Down
4 changes: 4 additions & 0 deletions compiler/vmops.nim
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
proc getCurrentExceptionWrapper(a: VmArgs) {.nimcall.} =
setResult(a, a.currentException)

proc raiseDefectWrapper(a: VmArgs) {.nimcall.} =
discard

proc staticWalkDirImpl(path: string, relative: bool): PNode =
result = newNode(nkBracket)
for k, f in walkDir(path, relative):
Expand Down Expand Up @@ -263,6 +266,7 @@ proc registerAdditionalOps*(c: PCtx) =
wrap2si(readLines, ioop)
systemop getCurrentExceptionMsg
systemop getCurrentException
systemop raiseDefect
registerCallback c, "stdlib.staticos.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
Expand Down
3 changes: 2 additions & 1 deletion doc/destructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ other associated resources. Variables are destroyed via this hook when
they go out of scope or when the routine they were declared in is about
to return.

A `=destroy` hook is allowed to have a parameter of a `var T` or `T` type. Taking a `var T` type is deprecated. The prototype of this hook for a type `T` needs to be:
A `=destroy` hook is allowed to have a parameter of a `var T` or `T` type.
The prototype of this hook for a type `T` needs to be:

```nim
proc `=destroy`(x: T)
Expand Down
2 changes: 1 addition & 1 deletion doc/grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ objectBranch = 'of' exprList colcom objectPart
objectBranches = objectBranch (IND{=} objectBranch)*
(IND{=} 'elif' expr colcom objectPart)*
(IND{=} 'else' colcom objectPart)?
objectCase = 'case' declColonEquals ':'? COMMENT?
objectCase = 'case' (declColonEquals / pragma)? ':'? COMMENT?
(IND{>} objectBranches DED
| IND{=} objectBranches)
objectPart = IND{>} objectPart^+IND{=} DED
Expand Down
Loading