Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sem: re-sem all call arguments in macro output #1192

Draft
wants to merge 20 commits into
base: devel
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f58ce0a
sigmatch: re-sem operands without the `nfSem` flag
zerbina Feb 16, 2024
55862cd
debugging: display node flags in tracing output
zerbina Feb 16, 2024
a1ba6d1
semcall: restore borrow target search
zerbina Feb 16, 2024
0bd9a29
semexprs: better bracket rewrite reversion
zerbina Feb 16, 2024
9f60702
semexprs: fix `asBracketExpr` edge case
zerbina Feb 16, 2024
8c15fd5
semexprs: prevent re-sem of hidden derefs
zerbina Feb 16, 2024
19fc011
sigmatch: prevent re-typing of hidden nodes
zerbina Feb 16, 2024
9b1ec67
semexprs: make set constructor analysis roundtrip
zerbina Feb 16, 2024
fe48c71
semexprs: support retyping for `varargs` arguments
zerbina Feb 16, 2024
2d32c66
semexprs: support re-sem'ing of literal AST
zerbina Feb 16, 2024
1d349f2
semexprs: don't perform unnecessary tree copy
zerbina Feb 16, 2024
b528897
sigmatch: mark created `tyStatic` with `tfHasStatic`
zerbina Feb 16, 2024
fe4733f
sigmatch: don't update empty container types in-place
zerbina Feb 16, 2024
cecae1c
sigmatch: prevent `static` arguments from losing their type
zerbina Feb 16, 2024
00ecc17
semexprs: don't ignore hidden nodes
zerbina Feb 16, 2024
fb55c41
semexprs: work around sequence constructions
zerbina Feb 16, 2024
fc21905
sigmatch: don't re-sem default arguments
zerbina Feb 16, 2024
155ba45
sigmatch: mark constant-evaluated expression with `nfSem`
zerbina Feb 16, 2024
2fa828b
semcall: don't re-sem default values (2)
zerbina Feb 16, 2024
3cbc842
Merge branch 'devel' into sem-resem-operands
zerbina Aug 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions compiler/mir/proto_mir.nim
Original file line number Diff line number Diff line change
Expand Up @@ -693,12 +693,7 @@ proc exprToPmir(c: TranslateCtx, result: var seq[ProtoItem], n: PNode, sink: boo
recurse(selectWhenBranch(n, c.pickVm), sink)
of nkStmtListExpr:
recurse(n.lastSon, sink)
# HACK: inherit the type from the child node. This prevents incorrectly
# typed array constructions (a sem bug) from appearing to work
# correctly (see
# tests/lang_exprs/tempty_typed_expressions_issues.nim)
result.add ProtoItem(orig: n, typ: result[^1].typ, kind: pirStmtList)
#node pirStmtList
node pirStmtList
of nkPragmaBlock:
# the pragma is uninteresting here, just skip it
recurse(n.lastSon, sink)
Expand Down
11 changes: 9 additions & 2 deletions compiler/sem/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,10 @@ proc semResolvedCall(c: PContext, x: TCandidate,
instGenericConvertersSons(c, result, x)
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
result.typ = finalCallee.typ[0]
result = updateDefaultParams(c.config, result)
if gp.isGenericParams:
# default parameters only need to be updated for instantiated generic
# routines. For normal routines they're already correct
result = updateDefaultParams(c.config, result)

proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds, flags: TExprFlags): PNode {.nosinks.} =
Expand Down Expand Up @@ -739,7 +742,11 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
x.addSonSkipIntLit(t.baseOfDistinct(c.graph, c.idgen), c.idgen)
else:
x = t.baseOfDistinct(c.graph, c.idgen)
call.add(newNodeIT(nkEmpty, fn.info, x))
let arg = newNodeIT(nkEmpty, fn.info, x)
# mark the node as analysed so that operand analysis doesn't analyze it
# again
arg.flags.incl nfSem
call.add(arg)
if hasDistinct:
let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}
var resolved = semOverloadedCall(c, call, call, filter, {})
Expand Down
178 changes: 124 additions & 54 deletions compiler/sem/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = c.config.newError(n, PAstDiag(kind: adSemProcHasNoConcreteType))
elif result.typ.kind in {tyVar, tyLent}:
result = newDeref(result)
result.flags.incl nfSem
elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ = newTypeS(tyVoid, c)
else:
Expand Down Expand Up @@ -93,6 +94,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =

elif result.typ.kind in {tyVar, tyLent}:
result = newDeref(result)
result.flags.incl nfSem

proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExprCheck(c, n, flags)
Expand Down Expand Up @@ -1834,7 +1836,8 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
PAstDiag(kind: adSemExpectedIdentifierWithExprContext,
expr: n))

result.add copyTree(n[0])
# the operand was already sem'ed immediately prior. No need to copy it
result.add n[0]

if result[0].kind == nkError: # handle the potential ident error
result = c.config.wrapError(result)
Expand Down Expand Up @@ -2829,6 +2832,8 @@ proc semSizeof(c: PContext, n: PNode): PNode =
result = c.config.newError(n, PAstDiag(kind: adSemMagicExpectTypeOrValue,
magic: mSizeOf))

proc asBracketExpr(c: PContext; n: PNode): PNode

proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
# this is a hotspot in the compiler!
result = n
Expand Down Expand Up @@ -2913,6 +2918,14 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of mSizeOf:
markUsed(c, n.info, s)
result = semSizeof(c, setMs(n, s))
of mArrGet:
# this could be a rewritten subscript operation
let b = asBracketExpr(c, n)
if b.isNil:
# it's not
result = semDirectOp(c, n, flags)
else:
result = semArrayAccess(c, b, flags)
else:
result = semDirectOp(c, n, flags)

Expand Down Expand Up @@ -2986,49 +2999,66 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
if n.len == 0:
rawAddSon(result.typ, newTypeS(tyEmpty, c))
else:
var
typ: PType = nil
diag: PAstDiag # the error diagnostic, if an error occurred

result.sons.setLen(n.len)
# only semantic checking for all elements, later type checking:
var typ: PType = nil
for i in 0..<n.len:
if isRange(n[i]):
checkSonsLen(n[i], 3, c.config)
n[i][1] = semExprWithType(c, n[i][1])
n[i][2] = semExprWithType(c, n[i][2])
if typ == nil:
typ = skipTypes(n[i][1].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
n[i].typ = n[i][2].typ # range node needs type too
for i, it in n.pairs:
var elem: PType
if isRange(it):
checkSonsLen(it, 3, c.config)
result[i] =
newTreeI(nkRange, it.info,
semExprWithType(c, it[1]),
semExprWithType(c, it[2]))
elem = result[i][0].typ
elif n[i].kind == nkRange:
# already semchecked
if typ == nil:
typ = skipTypes(n[i][0].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
checkSonsLen(n[i], 2, c.config)
result[i] =
newTreeI(nkRange, it.info,
semExprWithType(c, it[0]),
semExprWithType(c, it[1]))
elem = result[i][0].typ
else:
n[i] = semExprWithType(c, n[i])
if typ == nil:
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
result[i] = semExprWithType(c, n[i])
elem = result[i].typ

if typ.isNil:
typ = skipTypes(elem, {tyGenericInst, tyOrdinal, tyAlias, tySink})

if not isOrdinalType(typ, allowEnumWithHoles=true):
localReport(c.config, n, reportSem rsemExpectedOrdinal)
if typ.kind != tyError:
diag = PAstDiag(kind: adSemExpectedOrdinal, nonOrdTyp: typ)
typ = makeRangeType(c, 0, MaxSetElements - 1, n.info)

elif lengthOrd(c.config, typ) > MaxSetElements:
typ = makeRangeType(c, 0, MaxSetElements - 1, n.info)

addSonSkipIntLit(result.typ, typ, c.idgen)
for i in 0..<n.len:
var m: PNode
let info = n[i].info
if isRange(n[i]):
m = newNodeI(nkRange, info)
m.add fitNode(c, typ, n[i][1], info)
m.add fitNode(c, typ, n[i][2], info)

elif n[i].kind == nkRange:
m = n[i] # already semchecked

var hasError = false
template inheritError(n: PNode): PNode =
let x = n
hasError = hasError or x.kind == nkError
x

# second pass: type checking and error detection
for i in 0..<result.len:
let info = result[i].info
case result[i].kind
of nkRange:
result[i][0] = inheritError fitNode(c, typ, result[i][0], info)
result[i][1] = inheritError fitNode(c, typ, result[i][1], info)
else:
m = fitNode(c, typ, n[i], info)
result[i] = inheritError fitNode(c, typ, result[i], info)

result.add m
# wrap with the appropriate error (or none)
if diag != nil:
result = c.config.newError(result, diag)
elif hasError:
result = c.config.wrapError(result)

proc semTableConstr(c: PContext, n: PNode): PNode =
# we simply transform ``{key: value, key2, key3: value}`` to
Expand Down Expand Up @@ -3401,47 +3431,61 @@ proc semExport(c: PContext, n: PNode): PNode =

s = nextOverloadIter(o, c, a)

func containsArrGet(n: PNode): bool =
## Analyses `n` for whether it is or could be the ``mArrGet`` magic.
case n.kind
of nkSymChoices:
for it in n.items:
if it.kind == nkSym and it.sym.magic == mArrGet:
result = true
break
of nkSym:
# can happen in rare cases
result = n.sym.magic == mArrGet
else:
result = false

proc shouldBeBracketExpr(n: PNode): bool =
assert n.kind in nkCallKinds
let a = n[0]
if a.kind in nkCallKinds:
let b = a[0]
if b.kind in nkSymChoices:
for i in 0..<b.len:
if b[i].kind == nkSym and b[i].sym.magic == mArrGet:
result = true
break
elif b.kind == nkSym and b.sym.magic == mArrGet:
# can happen in rare cases
result = true

if result:
if containsArrGet(a[0]):
let be = newNodeI(nkBracketExpr, n.info)
for i in 1..<a.len:
be.add(a[i])
n[0] = be

proc asBracketExpr(c: PContext; n: PNode): PNode =
proc isGeneric(c: PContext; n: PNode): bool =
if n.kind in {nkIdent, nkAccQuoted}:
# XXX: this guesswork is meant to figure out whether a ``[](x, y)``
# expression *could* have been a ``x[y]`` expression where `x` is a
# generic procedure
case n.kind
of nkIdent, nkAccQuoted:
let s = qualifiedLookUp(c, n, {})
if s.isError:
# XXX: move to propagating nkError, skError, and tyError
localReport(c.config, s.ast)
result = false
else:
result = s != nil and isGenericRoutineStrict(s)
of nkSym:
result = isGenericRoutineStrict(n.sym)
of nkSymChoices:
for it in n.items:
if isGenericRoutineStrict(it.sym):
result = true
break
else:
result = false

assert n.kind in nkCallKinds
if n.len > 1 and isGeneric(c, n[1]):
let b = n[0]
if b.kind in nkSymChoices:
for i in 0..<b.len:
if b[i].kind == nkSym and b[i].sym.magic == mArrGet:
result = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: result.add(n[i])
return result
if containsArrGet(b):
result = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: result.add(n[i])
return result
return nil

proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) =
Expand Down Expand Up @@ -3767,7 +3811,15 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkTupleConstr:
result = semTupleConstr(c, n, flags)
of nkCurly: result = semSetConstr(c, n)
of nkBracket: result = semArrayConstr(c, n, flags)
of nkBracket:
if n.typ == nil or (tfVarargs notin n.typ.flags and
n.typ.skipTypes(abstractInst).kind != tySequence):
result = semArrayConstr(c, n, flags)
else:
# HACK: there's not enough information here to re-type an already
# typed varargs container, so it gets passed on for
# ``sigmatch`` to take care of re-typing it
result = n
of nkObjConstr: result = semObjConstr(c, n, flags)
of nkLambdaKinds:
result = semProcAnnotation(c, n)
Expand All @@ -3789,17 +3841,29 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
checkSonsLen(n, 1, c.config)
result[0] = semAddrArg(c, n[0])
result.typ = makePtrType(c, result[0].typ)
of nkHiddenAddr, nkHiddenDeref:
of nkHiddenAddr:
checkSonsLen(n, 1, c.config)
n[0] = semExpr(c, n[0], flags)
result = semExpr(c, n[0], flags)
of nkHiddenDeref:
checkSonsLen(n, 1, c.config)
# the type of the deref can be synthesized, so try to keep it
let operand = semExpr(c, n[0], flags)
case operand.typ.kind
of tyVar, tyLent:
result = newTreeIT(nkHiddenDeref, n.info, operand.typ.base): operand
else:
# an error or something else. Don't complain and pass it on, the
# receiver will figure out what to do with it
result = operand
of nkCast: result = c.config.extract(semCast(c, n))
of nkIfExpr, nkIfStmt: result = semIf(c, n, flags)
of nkConv:
checkSonsLen(n, 2, c.config)
result = semConv(c, n)
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv:
checkSonsLen(n, 2, c.config)
considerGenSyms(c, n)
# discard the hidden conversion. It's restored again (or not) if necessary
result = semExpr(c, n[1], flags)
of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv:
checkSonsLen(n, 1, c.config)
considerGenSyms(c, n)
Expand All @@ -3808,7 +3872,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
considerGenSyms(c, n)
of nkCheckedFieldExpr:
checkMinSonsLen(n, 2, c.config)
considerGenSyms(c, n)
# discard the check; analysis of the field expression will restore it if
# necessary
result = semExpr(c, n[0], flags)
of nkTableConstr:
result = semTableConstr(c, n)
of nkClosedSymChoice, nkOpenSymChoice:
Expand Down Expand Up @@ -3894,6 +3960,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
c.p.localBindStmts.add n
else:
localReport(c.config, n, reportSem rsemInvalidBindContext)
of nkNimNodeLit:
checkSonsLen(n, 1, c.config)
# the content is raw AST, so there's nothing to validate
result = copyNodeWithKids(n)
of nkError:
discard "ignore errors for now"
else:
Expand Down
Loading
Loading