Files
Nim/compiler/semexprs.nim
Zahary Karadjov cbf66e99a8 Working test cases for the sophisticated matrix library example from the manual
Fixed the dot operator when used within return types (see tgenericdotrettype)
Fixed the matching of generic concepts aliases used with the implicit generics style
2017-03-24 16:59:47 +02:00

2481 lines
90 KiB
Nim

#
#
# The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# this module does the semantic checking for expressions
# included from sem.nim
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
flags: TExprFlags = {}): PNode =
markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
pushInfoContext(n.info)
result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags)
if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags)
popInfoContext()
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# same as 'semExprWithType' but doesn't check for proc vars
result = semExpr(c, n, flags + {efOperand})
#if result.kind == nkEmpty and result.typ.isNil:
# do not produce another redundant error message:
#raiseRecoverableError("")
# result = errorNode(c, n)
if result.typ != nil:
# XXX tyGenericInst here?
if result.typ.kind == tyProc and tfUnresolved in result.typ.flags:
localError(n.info, errProcHasNoConcreteType, n.renderTree)
if result.typ.kind == tyVar: result = newDeref(result)
elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ = newTypeS(tyVoid, c)
else:
localError(n.info, errExprXHasNoType,
renderTree(result, {renderNoComments}))
result.typ = errorType(c)
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExpr(c, n, flags+{efWantValue})
if result.isNil or result.kind == nkEmpty:
# do not produce another redundant error message:
#raiseRecoverableError("")
result = errorNode(c, n)
if result.typ == nil or result.typ == enforceVoidContext:
# we cannot check for 'void' in macros ...
localError(n.info, errExprXHasNoType,
renderTree(result, {renderNoComments}))
result.typ = errorType(c)
else:
if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
if result.typ.kind == tyVar: result = newDeref(result)
semDestructorCheck(c, result, flags)
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExpr(c, n, flags)
if result.kind == nkEmpty:
# do not produce another redundant error message:
result = errorNode(c, n)
if result.typ == nil:
localError(n.info, errExprXHasNoType,
renderTree(result, {renderNoComments}))
result.typ = errorType(c)
else:
semProcvarCheck(c, result)
semDestructorCheck(c, result, flags)
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = symChoice(c, n, s, scClosed)
proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
result = copyTree(s.ast)
if result.isNil:
localError(n.info, "constant of type '" & typeToString(s.typ) & "' has no value")
result = newSymNode(s)
else:
result.typ = s.typ
result.info = n.info
type
TConvStatus = enum
convOK,
convNotNeedeed,
convNotLegal
proc checkConversionBetweenObjects(castDest, src: PType; pointers: int): TConvStatus =
let diff = inheritanceDiff(castDest, src)
return if diff == high(int) or (pointers > 1 and diff != 0):
convNotLegal
else:
convOK
const
IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64}
proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
result = convOK
if sameType(castDest, src) and castDest.sym == src.sym:
# don't annoy conversions that may be needed on another processor:
if castDest.kind notin IntegralTypes+{tyRange}:
result = convNotNeedeed
return
var d = skipTypes(castDest, abstractVar)
var s = skipTypes(src, abstractVar-{tyTypeDesc})
var pointers = 0
while (d != nil) and (d.kind in {tyPtr, tyRef}) and (d.kind == s.kind):
d = d.lastSon
s = s.lastSon
inc pointers
if d == nil:
result = convNotLegal
elif d.kind == tyObject and s.kind == tyObject:
result = checkConversionBetweenObjects(d, s, pointers)
elif (skipTypes(castDest, abstractVarRange).kind in IntegralTypes) and
(skipTypes(src, abstractVarRange-{tyTypeDesc}).kind in IntegralTypes):
# accept conversion between integral types
discard
else:
# we use d, s here to speed up that operation a bit:
case cmpTypes(c, d, s)
of isNone, isGeneric:
if not compareTypes(castDest, src, dcEqIgnoreDistinct):
result = convNotLegal
else:
discard
proc isCastable(dst, src: PType): bool =
## Checks whether the source type can be cast to the destination type.
## Casting is very unrestrictive; casts are allowed as long as
## castDest.size >= src.size, and typeAllowed(dst, skParam)
#const
# castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString,
# tySequence, tyPointer, tyNil, tyOpenArray,
# tyProc, tySet, tyEnum, tyBool, tyChar}
let src = src.skipTypes(tyUserTypeClasses)
if skipTypes(dst, abstractInst-{tyOpenArray}).kind == tyOpenArray:
return false
if skipTypes(src, abstractInst-{tyTypeDesc}).kind == tyTypeDesc:
return false
var dstSize, srcSize: BiggestInt
dstSize = computeSize(dst)
srcSize = computeSize(src)
if dstSize < 0:
result = false
elif srcSize < 0:
result = false
elif typeAllowed(dst, skParam) != nil:
result = false
else:
result = (dstSize >= srcSize) or
(skipTypes(dst, abstractInst).kind in IntegralTypes) or
(skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes)
if result and src.kind == tyNil:
result = dst.size <= platform.ptrSize
proc isSymChoice(n: PNode): bool {.inline.} =
result = n.kind in nkSymChoices
proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
# XXX: liftParamType started to perform addDecl
# we could do that instead in semTypeNode by snooping for added
# gnrc. params, then it won't be necessary to open a new scope here
openScope(c)
var lifted = liftParamType(c, skType, newNodeI(nkArgList, info),
t, ":anon", info)
closeScope(c)
if lifted != nil: t = lifted
proc semConv(c: PContext, n: PNode): PNode =
if sonsLen(n) != 2:
localError(n.info, errConvNeedsOneArg)
return n
result = newNodeI(nkConv, n.info)
var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
maybeLiftType(targetType, c, n[0].info)
result.addSon copyTree(n.sons[0])
var op = semExprWithType(c, n.sons[1])
if targetType.isMetaType:
let final = inferWithMetatype(c, targetType, op, true)
result.addSon final
result.typ = final.typ
return
result.typ = targetType
addSon(result, op)
if not isSymChoice(op):
let status = checkConvertible(c, result.typ, op.typ)
case status
of convOK:
# handle SomeProcType(SomeGenericProc)
if op.kind == nkSym and op.sym.isGenericRoutine:
result.sons[1] = fitNode(c, result.typ, result.sons[1], result.info)
elif op.kind == nkPar and targetType.kind == tyTuple:
op = fitNode(c, targetType, op, result.info)
of convNotNeedeed:
message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
of convNotLegal:
result = fitNode(c, result.typ, result.sons[1], result.info)
if result == nil:
localError(n.info, errGenerated, msgKindToString(errIllegalConvFromXtoY)%
[op.typ.typeToString, result.typ.typeToString])
else:
for i in countup(0, sonsLen(op) - 1):
let it = op.sons[i]
let status = checkConvertible(c, result.typ, it.typ)
if status in {convOK, convNotNeedeed}:
markUsed(n.info, it.sym, c.graph.usageSym)
styleCheckUse(n.info, it.sym)
markIndirect(c, it.sym)
return it
errorUseQualifier(c, n.info, op.sons[0].sym)
proc semCast(c: PContext, n: PNode): PNode =
## Semantically analyze a casting ("cast[type](param)")
checkSonsLen(n, 2)
let targetType = semTypeNode(c, n.sons[0], nil)
let castedExpr = semExprWithType(c, n.sons[1])
if tfHasMeta in targetType.flags:
localError(n.sons[0].info, errCastToANonConcreteType, $targetType)
if not isCastable(targetType, castedExpr.typ):
localError(n.info, errExprCannotBeCastToX, $targetType)
result = newNodeI(nkCast, n.info)
result.typ = targetType
addSon(result, copyTree(n.sons[0]))
addSon(result, castedExpr)
proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
const
opToStr: array[mLow..mHigh, string] = ["low", "high"]
if sonsLen(n) != 2:
localError(n.info, errXExpectsTypeOrValue, opToStr[m])
else:
n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
var typ = skipTypes(n.sons[1].typ, abstractVarRange +
{tyTypeDesc, tyFieldAccessor})
case typ.kind
of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
n.typ = getSysType(tyInt)
of tyArray:
n.typ = typ.sons[0] # indextype
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
# do not skip the range!
n.typ = n.sons[1].typ.skipTypes(abstractVar + {tyFieldAccessor})
of tyGenericParam:
# prepare this for resolving in semtypinst:
# we must use copyTree here in order to avoid creating a cycle
# that could easily turn into an infinite recursion in semtypinst
n.typ = makeTypeFromExpr(c, n.copyTree)
else:
localError(n.info, errInvalidArgForX, opToStr[m])
result = n
proc semSizeof(c: PContext, n: PNode): PNode =
if sonsLen(n) != 2:
localError(n.info, errXExpectsTypeOrValue, "sizeof")
else:
n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
#restoreOldStyleType(n.sons[1])
n.typ = getSysType(tyInt)
result = n
proc semOf(c: PContext, n: PNode): PNode =
if sonsLen(n) == 3:
n.sons[1] = semExprWithType(c, n.sons[1])
n.sons[2] = semExprWithType(c, n.sons[2], {efDetermineType})
#restoreOldStyleType(n.sons[1])
#restoreOldStyleType(n.sons[2])
let a = skipTypes(n.sons[1].typ, abstractPtrs)
let b = skipTypes(n.sons[2].typ, abstractPtrs)
let x = skipTypes(n.sons[1].typ, abstractPtrs-{tyTypeDesc})
let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc})
if x.kind == tyTypeDesc or y.kind != tyTypeDesc:
localError(n.info, errXExpectsObjectTypes, "of")
elif b.kind != tyObject or a.kind != tyObject:
localError(n.info, errXExpectsObjectTypes, "of")
else:
let diff = inheritanceDiff(a, b)
# | returns: 0 iff `a` == `b`
# | returns: -x iff `a` is the x'th direct superclass of `b`
# | returns: +x iff `a` is the x'th direct subclass of `b`
# | returns: `maxint` iff `a` and `b` are not compatible at all
if diff <= 0:
# optimize to true:
message(n.info, hintConditionAlwaysTrue, renderTree(n))
result = newIntNode(nkIntLit, 1)
result.info = n.info
result.typ = getSysType(tyBool)
return result
elif diff == high(int):
localError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a))
else:
localError(n.info, errXExpectsTwoArguments, "of")
n.typ = getSysType(tyBool)
result = n
proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
internalAssert n.sonsLen == 3 and
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = n[1].typ.skipTypes({tyTypeDesc, tyFieldAccessor})
if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize
of "closure":
let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and
tfIterator notin t.flags))
else:
result = newIntNode(nkIntLit, 0)
else:
var rhsOrigType = n[2].typ
var t2 = rhsOrigType.skipTypes({tyTypeDesc})
maybeLiftType(t2, c, n.info)
var m: TCandidate
initCandidate(c, m, t2)
if efExplain in flags: m.diagnostics = @[]
let match = typeRel(m, t2, t1) >= isSubtype # isNone
result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
if sonsLen(n) != 3:
localError(n.info, errXExpectsTwoArguments, "is")
result = n
n.typ = getSysType(tyBool)
n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
let t2 = semTypeNode(c, n[2], nil)
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
let lhsType = n[1].typ
if lhsType.kind != tyTypeDesc:
n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
elif lhsType.base.kind == tyNone:
# this is a typedesc variable, leave for evals
return
# BUGFIX: don't evaluate this too early: ``T is void``
if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
proc semOpAux(c: PContext, n: PNode) =
const flags = {efDetermineType}
for i in countup(1, n.sonsLen-1):
var a = n.sons[i]
if a.kind == nkExprEqExpr and sonsLen(a) == 2:
var info = a.sons[0].info
a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info)
a.sons[1] = semExprWithType(c, a.sons[1], flags)
a.typ = a.sons[1].typ
else:
n.sons[i] = semExprWithType(c, a, flags)
proc overloadedCallOpr(c: PContext, n: PNode): PNode =
# quick check if there is *any* () operator overloaded:
var par = getIdent("()")
if searchInScopes(c, par) == nil:
result = nil
else:
result = newNodeI(nkCall, n.info)
addSon(result, newIdentNode(par, n.info))
for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i])
result = semExpr(c, result)
proc changeType(n: PNode, newType: PType, check: bool) =
case n.kind
of nkCurly, nkBracket:
for i in countup(0, sonsLen(n) - 1):
changeType(n.sons[i], elemType(newType), check)
of nkPar:
let tup = newType.skipTypes({tyGenericInst, tyAlias})
if tup.kind != tyTuple:
if tup.kind == tyObject: return
globalError(n.info, "no tuple type for constructor")
elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
# named tuple?
for i in countup(0, sonsLen(n) - 1):
var m = n.sons[i].sons[0]
if m.kind != nkSym:
globalError(m.info, "invalid tuple constructor")
return
if tup.n != nil:
var f = getSymFromList(tup.n, m.sym.name)
if f == nil:
globalError(m.info, "unknown identifier: " & m.sym.name.s)
return
changeType(n.sons[i].sons[1], f.typ, check)
else:
changeType(n.sons[i].sons[1], tup.sons[i], check)
else:
for i in countup(0, sonsLen(n) - 1):
changeType(n.sons[i], tup.sons[i], check)
when false:
var m = n.sons[i]
var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i])
addSon(a, newSymNode(newType.n.sons[i].sym))
addSon(a, m)
changeType(m, tup.sons[i], check)
of nkCharLit..nkUInt64Lit:
if check and n.kind != nkUInt64Lit:
let value = n.intVal
if value < firstOrd(newType) or value > lastOrd(newType):
localError(n.info, errGenerated, "cannot convert " & $value &
" to " & typeToString(newType))
else: discard
n.typ = newType
proc arrayConstrType(c: PContext, n: PNode): PType =
var typ = newTypeS(tyArray, c)
rawAddSon(typ, nil) # index type
if sonsLen(n) == 0:
rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
else:
var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal, tyAlias})
addSonSkipIntLit(typ, t)
typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info)
result = typ
proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = newNodeI(nkBracket, n.info)
result.typ = newTypeS(tyArray, c)
rawAddSon(result.typ, nil) # index type
if sonsLen(n) == 0:
rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
else:
var x = n.sons[0]
var lastIndex: BiggestInt = 0
var indexType = getSysType(tyInt)
if x.kind == nkExprColonExpr and sonsLen(x) == 2:
var idx = semConstExpr(c, x.sons[0])
lastIndex = getOrdValue(idx)
indexType = idx.typ
x = x.sons[1]
let yy = semExprWithType(c, x)
var typ = yy.typ
addSon(result, yy)
#var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal})
for i in countup(1, sonsLen(n) - 1):
x = n.sons[i]
if x.kind == nkExprColonExpr and sonsLen(x) == 2:
var idx = semConstExpr(c, x.sons[0])
idx = fitNode(c, indexType, idx, x.info)
if lastIndex+1 != getOrdValue(idx):
localError(x.info, errInvalidOrderInArrayConstructor)
x = x.sons[1]
let xx = semExprWithType(c, x, flags*{efAllowDestructor})
result.add xx
typ = commonType(typ, xx.typ)
#n.sons[i] = semExprWithType(c, x, flags*{efAllowDestructor})
#addSon(result, fitNode(c, typ, n.sons[i]))
inc(lastIndex)
addSonSkipIntLit(result.typ, typ)
for i in 0 .. <result.len:
result.sons[i] = fitNode(c, typ, result.sons[i], result.sons[i].info)
result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info)
proc fixAbstractType(c: PContext, n: PNode) =
for i in 1 .. < n.len:
let it = n.sons[i]
# do not get rid of nkHiddenSubConv for OpenArrays, the codegen needs it:
if it.kind == nkHiddenSubConv and
skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}:
if skipTypes(it.sons[1].typ, abstractVar).kind in
{tyNil, tyTuple, tySet} or it[1].isArrayConstr:
var s = skipTypes(it.typ, abstractVar)
if s.kind != tyExpr:
changeType(it.sons[1], s, check=true)
n.sons[i] = it.sons[1]
proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or
sfCompileToCpp in c.module.flags):
checkSonsLen(n, 1)
result = n.sons[0]
else:
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
addSon(result, n)
if isAssignable(c, n) notin {arLValue, arLocalLValue}:
localError(n.info, errVarForOutParamNeeded)
proc analyseIfAddressTaken(c: PContext, n: PNode): 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 != tyVar:
incl(n.sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n)
of nkDotExpr:
checkSonsLen(n, 2)
if n.sons[1].kind != nkSym:
internalError(n.info, "analyseIfAddressTaken")
return
if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar:
incl(n.sons[1].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n)
of nkBracketExpr:
checkMinSonsLen(n, 1)
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind != tyVar:
if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n)
else:
result = newHiddenAddrTaken(c, n)
proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
checkMinSonsLen(n, 1)
const
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy}
# 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.sons[0].typ.skipTypes({tyGenericInst, tyAlias})
if n.sons[0].kind == nkSym and n.sons[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 countup(1, sonsLen(n) - 1):
if i < sonsLen(t) and t.sons[i] != nil and
skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
if isAssignable(c, n.sons[i]) notin {arLValue, arLocalLValue}:
if n.sons[i].kind != nkHiddenAddr:
localError(n.sons[i].info, errVarForOutParamNeeded)
return
for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkHiddenCallConv:
# we need to recurse explicitly here as converters can create nested
# calls and then they wouldn't be analysed otherwise
analyseIfAddressTakenInCall(c, n.sons[i])
semProcvarCheck(c, n.sons[i])
if i < sonsLen(t) and
skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
if n.sons[i].kind != nkHiddenAddr:
n.sons[i] = analyseIfAddressTaken(c, n.sons[i])
include semmagic
proc evalAtCompileTime(c: PContext, n: PNode): PNode =
result = n
if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return
var callee = n.sons[0].sym
# workaround for bug #537 (overly aggressive inlining leading to
# wrong NimNode semantics):
if n.typ != nil and tfTriggersCompileTime in n.typ.flags: return
# constant folding that is necessary for correctness of semantic pass:
if callee.magic != mNone and callee.magic in ctfeWhitelist and n.typ != nil:
var call = newNodeIT(nkCall, n.info, n.typ)
call.add(n.sons[0])
var allConst = true
for i in 1 .. < n.len:
var a = getConstExpr(c.module, n.sons[i])
if a == nil:
allConst = false
a = n.sons[i]
if a.kind == nkHiddenStdConv: a = a.sons[1]
call.add(a)
if allConst:
result = semfold.getConstExpr(c.module, call)
if result.isNil: result = n
else: return result
result.typ = semfold.getIntervalType(callee.magic, call)
block maybeLabelAsStatic:
# XXX: temporary work-around needed for tlateboundstatic.
# This is certainly not correct, but it will get the job
# done until we have a more robust infrastructure for
# implicit statics.
if n.len > 1:
for i in 1 .. <n.len:
# see bug #2113, it's possible that n[i].typ for errornous code:
if n[i].typ.isNil or n[i].typ.kind != tyStatic or
tfUnresolved notin n[i].typ.flags:
break maybeLabelAsStatic
n.typ = newTypeWithSons(c, tyStatic, @[n.typ])
n.typ.flags.incl tfUnresolved
# optimization pass: not necessary for correctness of the semantic pass
if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and
{sfForward, sfImportc} * callee.flags == {} and n.typ != nil:
if sfCompileTime notin callee.flags and
optImplicitStatic notin gOptions: return
if callee.magic notin ctfeWhitelist: return
if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine:
return
if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return
var call = newNodeIT(nkCall, n.info, n.typ)
call.add(n.sons[0])
for i in 1 .. < n.len:
let a = getConstExpr(c.module, n.sons[i])
if a == nil: return n
call.add(a)
#echo "NOW evaluating at compile time: ", call.renderTree
if sfCompileTime in callee.flags:
result = evalStaticExpr(c.module, c.cache, call, c.p.owner)
if result.isNil:
localError(n.info, errCannotInterpretNodeX, renderTree(call))
else: result = fixupTypeAfterEval(c, result, n)
else:
result = evalConstExpr(c.module, c.cache, call)
if result.isNil: result = n
else: result = fixupTypeAfterEval(c, result, n)
#if result != n:
# echo "SUCCESS evaluated at compile time: ", call.renderTree
proc semStaticExpr(c: PContext, n: PNode): PNode =
let a = semExpr(c, n.sons[0])
if a.findUnresolvedStatic != nil: return a
result = evalStaticExpr(c.module, c.cache, a, c.p.owner)
if result.isNil:
localError(n.info, errCannotInterpretNodeX, renderTree(n))
result = emptyNode
else:
result = fixupTypeAfterEval(c, result, a)
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags): PNode =
if flags*{efInTypeof, efWantIterator} != {}:
# consider: 'for x in pReturningArray()' --> we don't want the restriction
# to 'skIterator' anymore; skIterator is preferred in sigmatch already
# for typeof support.
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
result = semOverloadedCall(c, n, nOrig,
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
else:
result = semOverloadedCall(c, n, nOrig,
{skProc, skMethod, skConverter, skMacro, skTemplate}, flags)
if result != nil:
if result.sons[0].kind != nkSym:
internalError("semOverloadedCallAnalyseEffects")
return
let callee = result.sons[0].sym
case callee.kind
of skMacro, skTemplate: discard
else:
if callee.kind == skIterator and callee.id == c.p.owner.id:
localError(n.info, errRecursiveDependencyX, callee.name.s)
# error correction, prevents endless for loop elimination in transf.
# See bug #2051:
result.sons[0] = newSymNode(errorSym(c, n))
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
t: PType): TCandidate =
initCandidate(c, result, t)
matches(c, n, nOrig, result)
if result.state != csMatch:
# try to deref the first argument:
if experimentalMode(c) and canDeref(n):
n.sons[1] = n.sons[1].tryDeref
initCandidate(c, result, t)
matches(c, n, nOrig, result)
proc bracketedMacro(n: PNode): PSym =
if n.len >= 1 and n[0].kind == nkSym:
result = n[0].sym
if result.kind notin {skMacro, skTemplate}:
result = nil
proc semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym;
flags: TExprFlags): PNode =
# We received untransformed bracket expression coming from macroOrTmpl[].
# Transform it to macro or template call, where first come normal
# arguments, next come generic template arguments.
var sons = newSeq[PNode]()
sons.add inner.sons[0]
# Normal arguments:
for i in 1..<outer.len:
sons.add outer.sons[i]
# Generic template arguments from bracket expression:
for i in 1..<inner.len:
sons.add inner.sons[i]
shallowCopy(outer.sons, sons)
# FIXME: Shouldn't we check sfImmediate and call semDirectOp?
# However passing to semDirectOp doesn't work here.
case s.kind
of skMacro: result = semMacroExpr(c, outer, outer, s, flags)
of skTemplate: result = semTemplateExpr(c, outer, s, flags)
else: assert(false)
return
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
result = n
let callee = result.sons[0].sym
case callee.kind
of skMacro: result = semMacroExpr(c, result, orig, callee, flags)
of skTemplate: result = semTemplateExpr(c, result, callee, flags)
else:
semFinishOperands(c, result)
activate(c, result)
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
if callee.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
if c.inTypeClass == 0:
result = evalAtCompileTime(c, result)
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = nil
checkMinSonsLen(n, 1)
var prc = n.sons[0]
if n.sons[0].kind == nkDotExpr:
checkSonsLen(n.sons[0], 2)
let n0 = semFieldAccess(c, n.sons[0])
if n0.kind == nkDotCall:
# it is a static call!
result = n0
result.kind = nkCall
result.flags.incl nfExplicitCall
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
return semExpr(c, result, flags)
else:
n.sons[0] = n0
else:
n.sons[0] = semExpr(c, n.sons[0], {efInCall})
let t = n.sons[0].typ
if t != nil and t.kind == tyVar:
n.sons[0] = newDeref(n.sons[0])
elif n.sons[0].kind == nkBracketExpr:
let s = bracketedMacro(n.sons[0])
if s != nil:
return semBracketedMacro(c, n, n.sons[0], s, flags)
let nOrig = n.copyTree
semOpAux(c, n)
var t: PType = nil
if n.sons[0].typ != nil:
t = skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc})
if t != nil and t.kind == tyProc:
# This is a proc variable, apply normal overload resolution
let m = resolveIndirectCall(c, n, nOrig, t)
if m.state != csMatch:
if errorOutputs == {}:
# speed up error generation:
globalError(n.info, errTypeMismatch, "")
return emptyNode
else:
var hasErrorType = false
var msg = msgKindToString(errTypeMismatch)
for i in countup(1, sonsLen(n) - 1):
if i > 1: add(msg, ", ")
let nt = n.sons[i].typ
add(msg, typeToString(nt))
if nt.kind == tyError:
hasErrorType = true
break
if not hasErrorType:
add(msg, ")\n" & msgKindToString(errButExpected) & "\n" &
typeToString(n.sons[0].typ))
localError(n.info, errGenerated, msg)
return errorNode(c, n)
result = nil
else:
result = m.call
instGenericConvertersSons(c, result, m)
elif t != nil and t.kind == tyTypeDesc:
if n.len == 1: return semObjConstr(c, n, flags)
return semConv(c, n)
else:
result = overloadedCallOpr(c, n)
# Now that nkSym does not imply an iteration over the proc/iterator space,
# the old ``prc`` (which is likely an nkIdent) has to be restored:
if result == nil:
# XXX: hmm, what kind of symbols will end up here?
# do we really need to try the overload resolution?
n.sons[0] = prc
nOrig.sons[0] = prc
n.flags.incl nfExprCall
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result == nil: return errorNode(c, n)
elif result.kind notin nkCallKinds:
# the semExpr() in overloadedCallOpr can even break this condition!
# See bug #904 of how to trigger it:
return result
#result = afterCallActions(c, result, nOrig, flags)
if result.sons[0].kind == nkSym:
result = afterCallActions(c, result, nOrig, flags)
else:
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this seems to be a hotspot in the compiler!
let nOrig = n.copyTree
#semLazyOpAux(c, n)
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result != nil: result = afterCallActions(c, result, nOrig, flags)
else: result = errorNode(c, n)
proc buildEchoStmt(c: PContext, n: PNode): PNode =
# we MUST not check 'n' for semantics again here! But for now we give up:
result = newNodeI(nkCall, n.info)
var e = strTableGet(magicsys.systemModule.tab, getIdent"echo")
if e != nil:
add(result, newSymNode(e))
else:
localError(n.info, errSystemNeeds, "echo")
add(result, errorNode(c, n))
add(result, n)
result = semExpr(c, result)
proc semExprNoType(c: PContext, n: PNode): PNode =
result = semExpr(c, n, {efWantStmt})
discardCheck(c, result)
proc isTypeExpr(n: PNode): bool =
case n.kind
of nkType, nkTypeOfExpr: result = true
of nkSym: result = n.sym.kind == skType
else: result = false
proc createSetType(c: PContext; baseType: PType): PType =
assert baseType != nil
result = newTypeS(tySet, c)
rawAddSon(result, baseType)
proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
check: var PNode): PSym =
# transform in a node that contains the runtime check for the
# field, if it is in a case-part...
result = nil
case r.kind
of nkRecList:
for i in countup(0, sonsLen(r) - 1):
result = lookupInRecordAndBuildCheck(c, n, r.sons[i], field, check)
if result != nil: return
of nkRecCase:
checkMinSonsLen(r, 2)
if (r.sons[0].kind != nkSym): illFormedAst(r)
result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check)
if result != nil: return
let setType = createSetType(c, r.sons[0].typ)
var s = newNodeIT(nkCurly, r.info, setType)
for i in countup(1, sonsLen(r) - 1):
var it = r.sons[i]
case it.kind
of nkOfBranch:
result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check)
if result == nil:
for j in 0..sonsLen(it)-2: addSon(s, copyTree(it.sons[j]))
else:
if check == nil:
check = newNodeI(nkCheckedFieldExpr, n.info)
addSon(check, ast.emptyNode) # make space for access node
s = newNodeIT(nkCurly, n.info, setType)
for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j]))
var inExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
addSon(inExpr, newSymNode(opContains, n.info))
addSon(inExpr, s)
addSon(inExpr, copyTree(r.sons[0]))
addSon(check, inExpr)
#addSon(check, semExpr(c, inExpr))
return
of nkElse:
result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check)
if result != nil:
if check == nil:
check = newNodeI(nkCheckedFieldExpr, n.info)
addSon(check, ast.emptyNode) # make space for access node
var inExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
addSon(inExpr, newSymNode(opContains, n.info))
addSon(inExpr, s)
addSon(inExpr, copyTree(r.sons[0]))
var notExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
addSon(notExpr, newSymNode(opNot, n.info))
addSon(notExpr, inExpr)
addSon(check, notExpr)
return
else: illFormedAst(it)
of nkSym:
if r.sym.name.id == field.id: result = r.sym
else: illFormedAst(n)
proc makeDeref(n: PNode): PNode =
var t = skipTypes(n.typ, {tyGenericInst, tyAlias})
result = n
if t.kind == tyVar:
result = newNodeIT(nkHiddenDeref, n.info, t.sons[0])
addSon(result, n)
t = skipTypes(t.sons[0], {tyGenericInst, tyAlias})
while t.kind in {tyPtr, tyRef}:
var a = result
let baseTyp = t.lastSon
result = newNodeIT(nkHiddenDeref, n.info, baseTyp)
addSon(result, a)
t = skipTypes(baseTyp, {tyGenericInst, tyAlias})
const
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias}
proc readTypeParameter(c: PContext, typ: PType,
paramName: PIdent, info: TLineInfo): PNode =
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst}:
for statement in typ.n:
case statement.kind
of nkTypeSection:
for def in statement:
if def[0].sym.name.id == paramName.id:
# XXX: Instead of lifting the section type to a typedesc
# here, we could try doing it earlier in semTypeSection.
# This seems semantically correct and then we'll be able
# to return the section symbol directly here
let foundType = makeTypeDesc(c, def[2].typ)
return newSymNode(copySym(def[0].sym).linkTo(foundType), info)
of nkConstSection:
for def in statement:
if def[0].sym.name.id == paramName.id:
return def[2]
else:
discard
if typ.kind != tyUserTypeClass:
let ty = if typ.kind == tyCompositeTypeClass: typ.sons[1].skipGenericAlias
else: typ.skipGenericAlias
let tbody = ty.sons[0]
for s in countup(0, tbody.len-2):
let tParam = tbody.sons[s]
if tParam.sym.name.id == paramName.id:
let rawTyp = ty.sons[s + 1]
if rawTyp.kind == tyStatic:
return rawTyp.n
else:
let foundTyp = makeTypeDesc(c, rawTyp)
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
return nil
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
let s = getGenSym(c, sym)
case s.kind
of skConst:
markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind
of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyTuple, tySet, tyUInt..tyUInt64:
if s.magic == mNone: result = inlineConst(n, s)
else: result = newSymNode(s, n.info)
of tyArray, tySequence:
# Consider::
# const x = []
# proc p(a: openarray[int])
# proc q(a: openarray[char])
# p(x)
# q(x)
#
# It is clear that ``[]`` means two totally different things. Thus, we
# copy `x`'s AST into each context, so that the type fixup phase can
# deal with two different ``[]``.
if s.ast.len == 0: result = inlineConst(n, s)
else: result = newSymNode(s, n.info)
else:
result = newSymNode(s, n.info)
of skMacro:
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
result = newSymNode(s, n.info)
else:
result = semMacroExpr(c, n, n, s, flags)
of skTemplate:
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
result = newSymNode(s, n.info)
else:
result = semTemplateExpr(c, n, s, flags)
of skParam:
markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
if s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ...
return s.typ.n
elif sfGenSym in s.flags:
if c.p.wasForwarded:
# gensym'ed parameters that nevertheless have been forward declared
# need a special fixup:
let realParam = c.p.owner.typ.n[s.position+1]
internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam
return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info)
elif c.p.owner.kind == skMacro:
# gensym'ed macro parameters need a similar hack (see bug #1944):
var u = searchInScopes(c, s.name)
internalAssert u != nil and u.kind == skParam and u.owner == s.owner
return newSymNode(u, n.info)
result = newSymNode(s, n.info)
of skVar, skLet, skResult, skForVar:
if s.magic == mNimvm:
localError(n.info, "illegal context for 'nimvm' magic")
markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
result = newSymNode(s, n.info)
# We cannot check for access to outer vars for example because it's still
# not sure the symbol really ends up being used:
# var len = 0 # but won't be called
# genericThatUsesLen(x) # marked as taking a closure?
of skGenericParam:
styleCheckUse(n.info, s)
if s.typ.kind == tyStatic:
result = newSymNode(s, n.info)
result.typ = s.typ
elif s.ast != nil:
result = semExpr(c, s.ast)
else:
n.typ = s.typ
return n
of skType:
markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
if s.typ.kind == tyStatic and s.typ.n != nil:
return s.typ.n
result = newSymNode(s, n.info)
result.typ = makeTypeDesc(c, s.typ)
of skField:
var p = c.p
while p != nil and p.selfSym == nil:
p = p.next
if p != nil and p.selfSym != nil:
var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyPtr, tyRef,
tyAlias})
while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
var check: PNode = nil
if ty.kind == tyObject:
while true:
check = nil
let f = lookupInRecordAndBuildCheck(c, n, ty.n, s.name, check)
if f != nil and fieldVisible(c, f):
# is the access to a public field or in the same module or in a friend?
doAssert f == s
markUsed(n.info, f, c.graph.usageSym)
styleCheckUse(n.info, f)
result = newNodeIT(nkDotExpr, n.info, f.typ)
result.add makeDeref(newSymNode(p.selfSym))
result.add newSymNode(f) # we now have the correct field
if check != nil:
check.sons[0] = result
check.typ = result.typ
result = check
return result
if ty.sons[0] == nil: break
ty = skipTypes(ty.sons[0], skipPtrs)
# old code, not sure if it's live code:
markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
result = newSymNode(s, n.info)
else:
markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
result = newSymNode(s, n.info)
proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
## returns nil if it's not a built-in field access
checkSonsLen(n, 2)
# tests/bind/tbindoverload.nim wants an early exit here, but seems to
# work without now. template/tsymchoicefield doesn't like an early exit
# here at all!
#if isSymChoice(n.sons[1]): return
when defined(nimsuggest):
if gCmd == cmdIdeTools:
suggestExpr(c, n)
if exactEquals(gTrackPos, n[1].info): suggestExprNoCheck(c, n)
var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule})
if s != nil:
if s.kind in OverloadableSyms:
result = symChoice(c, n, s, scClosed)
if result.kind == nkSym: result = semSym(c, n, s, flags)
else:
markUsed(n.sons[1].info, s, c.graph.usageSym)
result = semSym(c, n, s, flags)
styleCheckUse(n.sons[1].info, s)
return
n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
#restoreOldStyleType(n.sons[0])
var i = considerQuotedIdent(n.sons[1])
var ty = n.sons[0].typ
var f: PSym = nil
result = nil
template tryReadingGenericParam(t: PType) =
case t.kind
of tyTypeParamsHolders:
return readTypeParameter(c, t, i, n.info)
of tyUserTypeClasses:
if t.isResolvedUserTypeClass:
return readTypeParameter(c, t, i, n.info)
else:
n.typ = makeTypeFromExpr(c, copyTree(n))
return n
of tyGenericParam:
n.typ = makeTypeFromExpr(c, copyTree(n))
return n
else:
discard
if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone):
if ty.kind == tyTypeDesc: ty = ty.base
ty = ty.skipTypes(tyDotOpTransparent)
case ty.kind
of tyEnum:
# look up if the identifier belongs to the enum:
while ty != nil:
f = getSymFromList(ty.n, i)
if f != nil: break
ty = ty.sons[0] # enum inheritance
if f != nil:
result = newSymNode(f)
result.info = n.info
result.typ = ty
markUsed(n.info, f, c.graph.usageSym)
styleCheckUse(n.info, f)
return
of tyObject, tyTuple:
if ty.n != nil and ty.n.kind == nkRecList:
let field = lookupInRecord(ty.n, i)
if field != nil:
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ])
n.typ.n = copyTree(n)
return n
else:
tryReadingGenericParam(ty)
return
# XXX: This is probably not relevant any more
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
ty = n.sons[0].typ
return nil
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias})
while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
var check: PNode = nil
if ty.kind == tyObject:
while true:
check = nil
f = lookupInRecordAndBuildCheck(c, n, ty.n, i, check)
if f != nil: break
if ty.sons[0] == nil: break
ty = skipTypes(ty.sons[0], skipPtrs)
if f != nil:
if fieldVisible(c, f):
# is the access to a public field or in the same module or in a friend?
markUsed(n.sons[1].info, f, c.graph.usageSym)
styleCheckUse(n.sons[1].info, f)
n.sons[0] = makeDeref(n.sons[0])
n.sons[1] = newSymNode(f) # we now have the correct field
n.typ = f.typ
if check == nil:
result = n
else:
check.sons[0] = n
check.typ = n.typ
result = check
elif ty.kind == tyTuple and ty.n != nil:
f = getSymFromList(ty.n, i)
if f != nil:
markUsed(n.sons[1].info, f, c.graph.usageSym)
styleCheckUse(n.sons[1].info, f)
n.sons[0] = makeDeref(n.sons[0])
n.sons[1] = newSymNode(f)
n.typ = f.typ
result = n
# we didn't find any field, let's look for a generic param
if result == nil:
let t = n.sons[0].typ.skipTypes(tyDotOpTransparent)
tryReadingGenericParam(t)
proc dotTransformation(c: PContext, n: PNode): PNode =
if isSymChoice(n.sons[1]):
result = newNodeI(nkDotCall, n.info)
addSon(result, n.sons[1])
addSon(result, copyTree(n[0]))
else:
var i = considerQuotedIdent(n.sons[1])
result = newNodeI(nkDotCall, n.info)
result.flags.incl nfDotField
addSon(result, newIdentNode(i, n[1].info))
addSon(result, copyTree(n[0]))
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this is difficult, because the '.' is used in many different contexts
# in Nim. We first allow types in the semantic checking.
result = builtinFieldAccess(c, n, flags)
if result == nil:
result = dotTransformation(c, n)
proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
result = newNodeI(nkCall, n.info)
result.add(newIdentNode(ident, n.info))
for i in 0 .. n.len-1: result.add(n[i])
proc semDeref(c: PContext, n: PNode): PNode =
checkSonsLen(n, 1)
n.sons[0] = semExprWithType(c, n.sons[0])
result = n
var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyAlias})
case t.kind
of tyRef, tyPtr: n.typ = t.lastSon
else: result = nil
#GlobalError(n.sons[0].info, errCircumNeedsPointer)
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
## returns nil if not a built-in subscript operator; also called for the
## checking of assignments
if sonsLen(n) == 1:
let x = semDeref(c, n)
if x == nil: return nil
result = newNodeIT(nkDerefExpr, x.info, x.typ)
result.add(x[0])
return
checkMinSonsLen(n, 2)
# make sure we don't evaluate generic macros/templates
n.sons[0] = semExprWithType(c, n.sons[0],
{efNoProcvarCheck, efNoEvaluateGeneric})
let arr = skipTypes(n.sons[0].typ, {tyGenericInst,
tyVar, tyPtr, tyRef, tyAlias})
case arr.kind
of tyArray, tyOpenArray, tyVarargs, tySequence, tyString,
tyCString:
if n.len != 2: return nil
n.sons[0] = makeDeref(n.sons[0])
c.p.bracketExpr = n.sons[0]
for i in countup(1, sonsLen(n) - 1):
n.sons[i] = semExprWithType(c, n.sons[i],
flags*{efInTypeof, efDetermineType})
var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(tyInt)
var arg = indexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1])
if arg != nil:
n.sons[1] = arg
result = n
result.typ = elemType(arr)
#GlobalError(n.info, errIndexTypesDoNotMatch)
of tyTypeDesc:
# The result so far is a tyTypeDesc bound
# a tyGenericBody. The line below will substitute
# it with the instantiated type.
result = n
result.typ = makeTypeDesc(c, semTypeNode(c, n, nil))
#result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
of tyTuple:
checkSonsLen(n, 2)
n.sons[0] = makeDeref(n.sons[0])
c.p.bracketExpr = n.sons[0]
# [] operator for tuples requires constant expression:
n.sons[1] = semConstExpr(c, n.sons[1])
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias}).kind in
{tyInt..tyInt64}:
var idx = getOrdValue(n.sons[1])
if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)]
else: localError(n.info, errInvalidIndexValueForTuple)
else:
localError(n.info, errIndexTypesDoNotMatch)
result = n
else:
let s = if n.sons[0].kind == nkSym: n.sons[0].sym
elif n[0].kind in nkSymChoices: n.sons[0][0].sym
else: nil
if s != nil:
case s.kind
of skProc, skMethod, skConverter, skIterator:
# type parameters: partial generic specialization
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
result = explicitGenericInstantiation(c, n, s)
of skMacro, skTemplate:
if efInCall in flags:
# We are processing macroOrTmpl[] in macroOrTmpl[](...) call.
# Return as is, so it can be transformed into complete macro or
# template call in semIndirectOp caller.
result = n
else:
# We are processing macroOrTmpl[] not in call. Transform it to the
# macro or template call with generic arguments here.
n.kind = nkCall
case s.kind
of skMacro: result = semMacroExpr(c, n, n, s, flags)
of skTemplate: result = semTemplateExpr(c, n, s, flags)
else: discard
of skType:
result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
else:
c.p.bracketExpr = n.sons[0]
else:
c.p.bracketExpr = n.sons[0]
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
let oldBracketExpr = c.p.bracketExpr
result = semSubscript(c, n, flags)
if result == nil:
# overloaded [] operator:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
c.p.bracketExpr = oldBracketExpr
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerQuotedIdent(a[1])
var setterId = newIdentNode(getIdent(id.s & '='), n.info)
# a[0] is already checked for semantics, that does ``builtinFieldAccess``
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
# nodes?
let aOrig = nOrig[0]
result = newNode(nkCall, n.info, sons = @[setterId, a[0],
semExprWithType(c, n[1])])
result.flags.incl nfDotSetter
let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]])
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
if result != nil:
result = afterCallActions(c, result, nOrig, {})
#fixAbstractType(c, result)
#analyseIfAddressTakenInCall(c, result)
proc takeImplicitAddr(c: PContext, n: PNode): PNode =
case n.kind
of nkHiddenAddr, nkAddr: return n
of nkHiddenDeref, nkDerefExpr: return n.sons[0]
of nkBracketExpr:
if len(n) == 1: return n.sons[0]
else: discard
var valid = isAssignable(c, n)
if valid != arLValue:
if valid == arLocalLValue:
localError(n.info, errXStackEscape, renderTree(n, {renderNoComments}))
else:
localError(n.info, errExprHasNoAddress)
result = newNodeIT(nkHiddenAddr, n.info, makePtrType(c, n.typ))
result.add(n)
proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
if le.kind == nkHiddenDeref:
var x = le.sons[0]
if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult:
n.sons[0] = x # 'result[]' --> 'result'
n.sons[1] = takeImplicitAddr(c, ri)
x.typ.flags.incl tfVarIsPtr
#echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
template resultTypeIsInferrable(typ: PType): untyped =
typ.isMetaType and typ.kind != tyTypeDesc
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
checkSonsLen(n, 2)
var a = n.sons[0]
case a.kind
of nkDotExpr:
# r.f = x
# --> `f=` (r, x)
let nOrig = n.copyTree
a = builtinFieldAccess(c, a, {efLValue})
if a == nil:
a = propertyWriteAccess(c, n, nOrig, n[0])
if a != nil: return a
# we try without the '='; proc that return 'var' or macros are still
# possible:
a = dotTransformation(c, n[0])
if a.kind == nkDotCall:
a.kind = nkCall
a = semExprWithType(c, a, {efLValue})
of nkBracketExpr:
# a[i] = x
# --> `[]=`(a, i, x)
let oldBracketExpr = c.p.bracketExpr
a = semSubscript(c, a, {efLValue})
if a == nil:
result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
add(result, n[1])
if mode == noOverloadedSubscript:
bracketNotFoundError(c, result)
return n
else:
result = semExprNoType(c, result)
c.p.bracketExpr = oldBracketExpr
return result
c.p.bracketExpr = oldBracketExpr
of nkCurlyExpr:
# a{i} = x --> `{}=`(a, i, x)
result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
add(result, n[1])
return semExprNoType(c, result)
of nkPar:
if a.len >= 2:
# unfortunately we need to rewrite ``(x, y) = foo()`` already here so
# that overloading of the assignment operator still works. Usually we
# prefer to do these rewritings in transf.nim:
return semStmt(c, lowerTupleUnpackingForAsgn(n, c.p.owner))
else:
a = semExprWithType(c, a, {efLValue})
else:
a = semExprWithType(c, a, {efLValue})
n.sons[0] = a
# a = b # both are vars, means: a[] = b[]
# a = b # b no 'var T' means: a = addr(b)
var le = a.typ
if (skipTypes(le, {tyGenericInst, tyAlias}).kind != tyVar and
isAssignable(c, a) == arNone) or
skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}:
# Direct assignment to a discriminant is allowed!
localError(a.info, errXCannotBeAssignedTo,
renderTree(a, {renderNoComments}))
else:
let
lhs = n.sons[0]
lhsIsResult = lhs.kind == nkSym and lhs.sym.kind == skResult
var
rhs = semExprWithType(c, n.sons[1],
if lhsIsResult: {efAllowDestructor} else: {})
if lhsIsResult:
n.typ = enforceVoidContext
if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
internalAssert c.p.resultSym != nil
lhs.typ = rhs.typ
c.p.resultSym.typ = rhs.typ
c.p.owner.typ.sons[0] = rhs.typ
else:
typeMismatch(n.info, lhs.typ, rhs.typ)
n.sons[1] = fitNode(c, le, rhs, n.info)
if tfHasAsgn in lhs.typ.flags and not lhsIsResult and
mode != noOverloadedAsgn:
return overloadedAsgn(c, lhs, n.sons[1])
fixAbstractType(c, n)
asgnToResultVar(c, n, n.sons[0], n.sons[1])
result = n
proc semReturn(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1)
if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or (
c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
if n.sons[0].kind != nkEmpty:
# transform ``return expr`` to ``result = expr; return``
if c.p.resultSym != nil:
var a = newNodeI(nkAsgn, n.sons[0].info)
addSon(a, newSymNode(c.p.resultSym))
addSon(a, n.sons[0])
n.sons[0] = semAsgn(c, a)
# optimize away ``result = result``:
if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym:
n.sons[0] = ast.emptyNode
else:
localError(n.info, errNoReturnTypeDeclared)
else:
localError(n.info, errXNotAllowedHere, "\'return\'")
proc semProcBody(c: PContext, n: PNode): PNode =
openScope(c)
result = semExpr(c, n)
if c.p.resultSym != nil and not isEmptyType(result.typ):
# transform ``expr`` to ``result = expr``, but not if the expr is already
# ``result``:
if result.kind == nkSym and result.sym == c.p.resultSym:
discard
elif result.kind == nkNilLit:
# or ImplicitlyDiscardable(result):
# new semantic: 'result = x' triggers the void context
result.typ = nil
elif result.kind == nkStmtListExpr and result.typ.kind == tyNil:
# to keep backwards compatibility bodies like:
# nil
# # comment
# are not expressions:
fixNilType(result)
else:
var a = newNodeI(nkAsgn, n.info, 2)
a.sons[0] = newSymNode(c.p.resultSym)
a.sons[1] = result
result = semAsgn(c, a)
else:
discardCheck(c, result)
if c.p.owner.kind notin {skMacro, skTemplate} and
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
if isEmptyType(result.typ):
# we inferred a 'void' return type:
c.p.resultSym.typ = errorType(c)
c.p.owner.typ.sons[0] = nil
else:
localError(c.p.resultSym.info, errCannotInferReturnType)
closeScope(c)
proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
var t = skipTypes(restype, {tyGenericInst, tyAlias})
case t.kind
of tyVar:
n.sons[0] = takeImplicitAddr(c, n.sons[0])
of tyTuple:
for i in 0.. <t.sonsLen:
var e = skipTypes(t.sons[i], {tyGenericInst, tyAlias})
if e.kind == tyVar:
if n.sons[0].kind == nkPar:
n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i])
elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and
n.sons[0].sons[1].kind == nkPar:
var a = n.sons[0].sons[1]
a.sons[i] = takeImplicitAddr(c, a.sons[i])
else:
localError(n.sons[0].info, errXExpected, "tuple constructor")
else: discard
proc semYield(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1)
if c.p.owner == nil or c.p.owner.kind != skIterator:
localError(n.info, errYieldNotAllowedHere)
elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
localError(n.info, errYieldNotAllowedInTryStmt)
elif n.sons[0].kind != nkEmpty:
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
var iterType = c.p.owner.typ
let restype = iterType.sons[0]
if restype != nil:
if restype.kind != tyExpr:
n.sons[0] = fitNode(c, restype, n.sons[0], n.info)
if n.sons[0].typ == nil: internalError(n.info, "semYield")
if resultTypeIsInferrable(restype):
let inferred = n.sons[0].typ
iterType.sons[0] = inferred
semYieldVarResult(c, n, restype)
else:
localError(n.info, errCannotReturnExpr)
elif c.p.owner.typ.sons[0] != nil:
localError(n.info, errGenerated, "yield statement must yield a value")
proc lookUpForDefined(c: PContext, i: PIdent, onlyCurrentScope: bool): PSym =
if onlyCurrentScope:
result = localSearchInScope(c, i)
else:
result = searchInScopes(c, i) # no need for stub loading
proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
case n.kind
of nkIdent:
result = lookUpForDefined(c, n.ident, onlyCurrentScope)
of nkDotExpr:
result = nil
if onlyCurrentScope: return
checkSonsLen(n, 2)
var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope)
if m != nil and m.kind == skModule:
let ident = considerQuotedIdent(n[1])
if m == c.module:
result = strTableGet(c.topLevelScope.symbols, ident)
else:
result = strTableGet(m.tab, ident)
of nkAccQuoted:
result = lookUpForDefined(c, considerQuotedIdent(n), onlyCurrentScope)
of nkSym:
result = n.sym
of nkOpenSymChoice, nkClosedSymChoice:
result = n.sons[0].sym
else:
localError(n.info, errIdentifierExpected, renderTree(n))
result = nil
proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
checkSonsLen(n, 2)
# we replace this node by a 'true' or 'false' node:
result = newIntNode(nkIntLit, 0)
if not onlyCurrentScope and considerQuotedIdent(n[0]).s == "defined":
if n.sons[1].kind != nkIdent:
localError(n.info, "obsolete usage of 'defined', use 'declared' instead")
elif condsyms.isDefined(n.sons[1].ident):
result.intVal = 1
elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil:
result.intVal = 1
result.info = n.info
result.typ = getSysType(tyBool)
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
## The argument to the proc should be nkCall(...) or similar
## Returns the macro/template symbol
if isCallExpr(n):
var expandedSym = qualifiedLookUp(c, n[0], {checkUndeclared})
if expandedSym == nil:
errorUndeclaredIdentifier(c, n.info, n[0].renderTree)
return errorSym(c, n[0])
if expandedSym.kind notin {skMacro, skTemplate}:
localError(n.info, errXisNoMacroOrTemplate, expandedSym.name.s)
return errorSym(c, n[0])
result = expandedSym
else:
localError(n.info, errXisNoMacroOrTemplate, n.renderTree)
result = errorSym(c, n)
proc expectString(c: PContext, n: PNode): string =
var n = semConstExpr(c, n)
if n.kind in nkStrKinds:
return n.strVal
else:
localError(n.info, errStringLiteralExpected)
proc getMagicSym(magic: TMagic): PSym =
result = newSym(skProc, getIdent($magic), systemModule, gCodegenLineInfo)
result.magic = magic
proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
result = newSym(kind, c.cache.idAnon, getCurrOwner(c), info)
result.flags = {sfGenSym}
proc semExpandToAst(c: PContext, n: PNode): PNode =
let macroCall = n[1]
when false:
let expandedSym = expectMacroOrTemplateCall(c, macroCall)
if expandedSym.kind == skError: return n
macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
markUsed(n.info, expandedSym, c.graph.usageSym)
styleCheckUse(n.info, expandedSym)
if isCallExpr(macroCall):
for i in countup(1, macroCall.len-1):
#if macroCall.sons[0].typ.sons[i].kind != tyExpr:
macroCall.sons[i] = semExprWithType(c, macroCall[i], {})
# performing overloading resolution here produces too serious regressions:
let headSymbol = macroCall[0]
var cands = 0
var cand: PSym = nil
var o: TOverloadIter
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind in {skTemplate, skMacro} and symx.typ.len == macroCall.len:
cand = symx
inc cands
symx = nextOverloadIter(o, c, headSymbol)
if cands == 0:
localError(n.info, "expected a template that takes " & $(macroCall.len-1) & " arguments")
elif cands >= 2:
localError(n.info, "ambiguous symbol in 'getAst' context: " & $macroCall)
else:
let info = macroCall.sons[0].info
macroCall.sons[0] = newSymNode(cand, info)
markUsed(info, cand, c.graph.usageSym)
styleCheckUse(info, cand)
# we just perform overloading resolution here:
#n.sons[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro})
else:
localError(n.info, "getAst takes a call, but got " & n.renderTree)
# Preserve the magic symbol in order to be handled in evals.nim
internalAssert n.sons[0].sym.magic == mExpandToAst
#n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType
n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
else: sysTypeFromName"PNimrodNode"
result = n
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
flags: TExprFlags = {}): PNode =
if sonsLen(n) == 2:
n.sons[0] = newSymNode(magicSym, n.info)
result = semExpandToAst(c, n)
else:
result = semDirectOp(c, n, flags)
proc processQuotations(n: var PNode, op: string,
quotes: var seq[PNode],
ids: var seq[PNode]) =
template returnQuote(q) =
quotes.add q
n = newIdentNode(getIdent($quotes.len), n.info)
ids.add n
return
if n.kind == nkPrefix:
checkSonsLen(n, 2)
if n[0].kind == nkIdent:
var examinedOp = n[0].ident.s
if examinedOp == op:
returnQuote n[1]
elif examinedOp.startsWith(op):
n.sons[0] = newIdentNode(getIdent(examinedOp.substr(op.len)), n.info)
elif n.kind == nkAccQuoted and op == "``":
returnQuote n[0]
for i in 0 .. <n.safeLen:
processQuotations(n.sons[i], op, quotes, ids)
proc semQuoteAst(c: PContext, n: PNode): PNode =
internalAssert n.len == 2 or n.len == 3
# We transform the do block into a template with a param for
# each interpolation. We'll pass this template to getAst.
var
doBlk = n{-1}
op = if n.len == 3: expectString(c, n[1]) else: "``"
quotes = newSeq[PNode](1)
# the quotes will be added to a nkCall statement
# leave some room for the callee symbol
ids = newSeq[PNode]()
# this will store the generated param names
if doBlk.kind != nkDo:
localError(n.info, errXExpected, "block")
processQuotations(doBlk.sons[bodyPos], op, quotes, ids)
doBlk.sons[namePos] = newAnonSym(c, skTemplate, n.info).newSymNode
if ids.len > 0:
doBlk.sons[paramsPos] = newNodeI(nkFormalParams, n.info)
doBlk[paramsPos].add getSysSym("typed").newSymNode # return type
ids.add getSysSym("untyped").newSymNode # params type
ids.add emptyNode # no default value
doBlk[paramsPos].add newNode(nkIdentDefs, n.info, ids)
var tmpl = semTemplateDef(c, doBlk)
quotes[0] = tmpl[namePos]
result = newNode(nkCall, n.info, @[
getMagicSym(mExpandToAst).newSymNode,
newNode(nkCall, n.info, quotes)])
result = semExpandToAst(c, result)
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# watch out, hacks ahead:
let oldErrorCount = msgs.gErrorCounter
let oldErrorMax = msgs.gErrorMax
let oldCompilesId = c.compilesContextId
inc c.compilesContextIdGenerator
c.compilesContextId = c.compilesContextIdGenerator
# do not halt after first error:
msgs.gErrorMax = high(int)
# open a scope for temporary symbol inclusions:
let oldScope = c.currentScope
openScope(c)
let oldOwnerLen = len(c.graph.owners)
let oldGenerics = c.generics
let oldErrorOutputs = errorOutputs
if efExplain notin flags: errorOutputs = {}
let oldContextLen = msgs.getInfoContextLen()
let oldInGenericContext = c.inGenericContext
let oldInUnrolledContext = c.inUnrolledContext
let oldInGenericInst = c.inGenericInst
let oldProcCon = c.p
c.generics = @[]
var err: string
try:
result = semExpr(c, n, flags)
if msgs.gErrorCounter != oldErrorCount: result = nil
except ERecoverableError:
discard
# undo symbol table changes (as far as it's possible):
c.compilesContextId = oldCompilesId
c.generics = oldGenerics
c.inGenericContext = oldInGenericContext
c.inUnrolledContext = oldInUnrolledContext
c.inGenericInst = oldInGenericInst
c.p = oldProcCon
msgs.setInfoContextLen(oldContextLen)
setLen(c.graph.owners, oldOwnerLen)
c.currentScope = oldScope
errorOutputs = oldErrorOutputs
msgs.gErrorCounter = oldErrorCount
msgs.gErrorMax = oldErrorMax
proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we replace this node by a 'true' or 'false' node:
if sonsLen(n) != 2: return semDirectOp(c, n, flags)
result = newIntNode(nkIntLit, ord(tryExpr(c, n[1], flags) != nil))
result.info = n.info
result.typ = getSysType(tyBool)
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
if sonsLen(n) == 3:
# XXX ugh this is really a hack: shallowCopy() can be overloaded only
# with procs that take not 2 parameters:
result = newNodeI(nkFastAsgn, n.info)
result.add(n[1])
result.add(n[2])
result = semAsgn(c, result)
else:
result = semDirectOp(c, n, flags)
proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType =
result = newType(tyGenericInvocation, c.module)
addSonSkipIntLit(result, magicsys.getCompilerProc("FlowVar").typ)
addSonSkipIntLit(result, t)
result = instGenericContainer(c, info, result, allowMetaTypes = false)
proc instantiateCreateFlowVarCall(c: PContext; t: PType;
info: TLineInfo): PSym =
let sym = magicsys.getCompilerProc("nimCreateFlowVar")
if sym == nil:
localError(info, errSystemNeeds, "nimCreateFlowVar")
var bindings: TIdTable
initIdTable(bindings)
bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t)
result = c.semGenerateInstance(c, sym, bindings, info)
# since it's an instantiation, we unmark it as a compilerproc. Otherwise
# codegen would fail:
if sfCompilerProc in result.flags:
result.flags = result.flags - {sfCompilerProc, sfExportC, sfImportC}
result.loc.r = nil
proc setMs(n: PNode, s: PSym): PNode =
result = n
n.sons[0] = newSymNode(s)
n.sons[0].info = n.info
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
# this is a hotspot in the compiler!
# DON'T forget to update ast.SpecialSemMagics if you add a magic here!
result = n
case s.magic # magics that need special treatment
of mAddr:
checkSonsLen(n, 2)
result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr")
of mTypeOf:
checkSonsLen(n, 2)
result = semTypeOf(c, n.sons[1])
#of mArrGet: result = semArrGet(c, n, flags)
#of mArrPut: result = semArrPut(c, n, flags)
#of mAsgn: result = semAsgnOpr(c, n)
of mDefined: result = semDefined(c, setMs(n, s), false)
of mDefinedInScope: result = semDefined(c, setMs(n, s), true)
of mCompiles: result = semCompiles(c, setMs(n, s), flags)
of mLow: result = semLowHigh(c, setMs(n, s), mLow)
of mHigh: result = semLowHigh(c, setMs(n, s), mHigh)
of mSizeOf: result = semSizeof(c, setMs(n, s))
of mIs: result = semIs(c, setMs(n, s), flags)
of mOf: result = semOf(c, setMs(n, s))
of mShallowCopy: result = semShallowCopy(c, n, flags)
of mExpandToAst: result = semExpandToAst(c, n, s, flags)
of mQuoteAst: result = semQuoteAst(c, n)
of mAstToStr:
checkSonsLen(n, 2)
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString)
of mParallel:
if not experimentalMode(c):
localError(n.info, "use the {.experimental.} pragma to enable 'parallel'")
result = setMs(n, s)
var x = n.lastSon
if x.kind == nkDo: x = x.sons[bodyPos]
inc c.inParallelStmt
result.sons[1] = semStmt(c, x)
dec c.inParallelStmt
of mSpawn:
result = setMs(n, s)
for i in 1 .. <n.len:
result.sons[i] = semExpr(c, n.sons[i])
let typ = result[^1].typ
if not typ.isEmptyType:
if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar:
result.typ = createFlowVar(c, typ, n.info)
else:
result.typ = typ
result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
else:
result.add emptyNode
of mProcCall:
result = setMs(n, s)
result.sons[1] = semExpr(c, n.sons[1])
result.typ = n[1].typ
of mPlugin:
# semDirectOp with conditional 'afterCallActions':
let nOrig = n.copyTree
#semLazyOpAux(c, n)
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result == nil:
result = errorNode(c, n)
else:
let callee = result.sons[0].sym
if callee.magic == mNone:
semFinishOperands(c, result)
activate(c, result)
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
if callee.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
else:
result = semDirectOp(c, n, flags)
proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
# If semCheck is set to false, ``when`` will return the verbatim AST of
# the correct branch. Otherwise the AST will be passed through semStmt.
result = nil
template setResult(e: untyped) =
if semCheck: result = semExpr(c, e) # do not open a new scope!
else: result = e
# Check if the node is "when nimvm"
# when nimvm:
# ...
# else:
# ...
var whenNimvm = false
var typ = commonTypeBegin
if n.sons.len == 2 and n.sons[0].kind == nkElifBranch and
n.sons[1].kind == nkElse:
let exprNode = n.sons[0].sons[0]
if exprNode.kind == nkIdent:
whenNimvm = lookUp(c, exprNode).magic == mNimvm
elif exprNode.kind == nkSym:
whenNimvm = exprNode.sym.magic == mNimvm
if whenNimvm: n.flags.incl nfLL
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
case it.kind
of nkElifBranch, nkElifExpr:
checkSonsLen(it, 2)
if whenNimvm:
if semCheck:
it.sons[1] = semExpr(c, it.sons[1])
typ = commonType(typ, it.sons[1].typ)
result = n # when nimvm is not elimited until codegen
else:
var e = semConstExpr(c, it.sons[0])
if e.kind != nkIntLit:
# can happen for cascading errors, assume false
# InternalError(n.info, "semWhen")
discard
elif e.intVal != 0 and result == nil:
setResult(it.sons[1])
of nkElse, nkElseExpr:
checkSonsLen(it, 1)
if result == nil or whenNimvm:
if semCheck:
it.sons[0] = semExpr(c, it.sons[0])
typ = commonType(typ, it.sons[0].typ)
if result == nil:
result = it.sons[0]
else: illFormedAst(n)
if result == nil:
result = newNodeI(nkEmpty, n.info)
if whenNimvm: result.typ = typ
# The ``when`` statement implements the mechanism for platform dependent
# code. Thus we try to ensure here consistent ID allocation after the
# ``when`` statement.
idSynchronizationPoint(200)
proc semSetConstr(c: PContext, n: PNode): PNode =
result = newNodeI(nkCurly, n.info)
result.typ = newTypeS(tySet, c)
if sonsLen(n) == 0:
rawAddSon(result.typ, newTypeS(tyEmpty, c))
else:
# only semantic checking for all elements, later type checking:
var typ: PType = nil
for i in countup(0, sonsLen(n) - 1):
if isRange(n.sons[i]):
checkSonsLen(n.sons[i], 3)
n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1])
n.sons[i].sons[2] = semExprWithType(c, n.sons[i].sons[2])
if typ == nil:
typ = skipTypes(n.sons[i].sons[1].typ,
{tyGenericInst, tyVar, tyOrdinal, tyAlias})
n.sons[i].typ = n.sons[i].sons[2].typ # range node needs type too
elif n.sons[i].kind == nkRange:
# already semchecked
if typ == nil:
typ = skipTypes(n.sons[i].sons[0].typ,
{tyGenericInst, tyVar, tyOrdinal, tyAlias})
else:
n.sons[i] = semExprWithType(c, n.sons[i])
if typ == nil:
typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyOrdinal, tyAlias})
if not isOrdinalType(typ):
localError(n.info, errOrdinalTypeExpected)
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
elif lengthOrd(typ) > MaxSetElements:
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
addSonSkipIntLit(result.typ, typ)
for i in countup(0, sonsLen(n) - 1):
var m: PNode
let info = n.sons[i].info
if isRange(n.sons[i]):
m = newNodeI(nkRange, info)
addSon(m, fitNode(c, typ, n.sons[i].sons[1], info))
addSon(m, fitNode(c, typ, n.sons[i].sons[2], info))
elif n.sons[i].kind == nkRange: m = n.sons[i] # already semchecked
else:
m = fitNode(c, typ, n.sons[i], info)
addSon(result, m)
proc semTableConstr(c: PContext, n: PNode): PNode =
# we simply transform ``{key: value, key2, key3: value}`` to
# ``[(key, value), (key2, value2), (key3, value2)]``
result = newNodeI(nkBracket, n.info)
var lastKey = 0
for i in 0..n.len-1:
var x = n.sons[i]
if x.kind == nkExprColonExpr and sonsLen(x) == 2:
for j in countup(lastKey, i-1):
var pair = newNodeI(nkPar, x.info)
pair.add(n.sons[j])
pair.add(x[1])
result.add(pair)
var pair = newNodeI(nkPar, x.info)
pair.add(x[0])
pair.add(x[1])
result.add(pair)
lastKey = i+1
if lastKey != n.len: illFormedAst(n)
result = semExpr(c, result)
type
TParKind = enum
paNone, paSingle, paTupleFields, paTuplePositions
proc checkPar(n: PNode): TParKind =
var length = sonsLen(n)
if length == 0:
result = paTuplePositions # ()
elif length == 1:
if n.sons[0].kind == nkExprColonExpr: result = paTupleFields
else: result = paSingle # (expr)
else:
if n.sons[0].kind == nkExprColonExpr: result = paTupleFields
else: result = paTuplePositions
for i in countup(0, length - 1):
if result == paTupleFields:
if (n.sons[i].kind != nkExprColonExpr) or
not (n.sons[i].sons[0].kind in {nkSym, nkIdent}):
localError(n.sons[i].info, errNamedExprExpected)
return paNone
else:
if n.sons[i].kind == nkExprColonExpr:
localError(n.sons[i].info, errNamedExprNotAllowed)
return paNone
proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = newNodeI(nkPar, n.info)
var typ = newTypeS(tyTuple, c)
typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs
var ids = initIntSet()
for i in countup(0, sonsLen(n) - 1):
if n[i].kind != nkExprColonExpr or n[i][0].kind notin {nkSym, nkIdent}:
illFormedAst(n.sons[i])
var id: PIdent
if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident
else: id = n.sons[i].sons[0].sym.name
if containsOrIncl(ids, id.id):
localError(n.sons[i].info, errFieldInitTwice, id.s)
n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1],
flags*{efAllowDestructor})
var f = newSymS(skField, n.sons[i].sons[0], c)
f.typ = skipIntLit(n.sons[i].sons[1].typ)
f.position = i
rawAddSon(typ, f.typ)
addSon(typ.n, newSymNode(f))
n.sons[i].sons[0] = newSymNode(f)
addSon(result, n.sons[i])
result.typ = typ
proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = n # we don't modify n, but compute the type:
var typ = newTypeS(tyTuple, c) # leave typ.n nil!
for i in countup(0, sonsLen(n) - 1):
n.sons[i] = semExprWithType(c, n.sons[i], flags*{efAllowDestructor})
addSonSkipIntLit(typ, n.sons[i].typ)
result.typ = typ
proc isTupleType(n: PNode): bool =
if n.len == 0:
return false # don't interpret () as type
for i in countup(0, n.len - 1):
if n[i].typ == nil or n[i].typ.kind != tyTypeDesc:
return false
return true
proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
checkInitialized(n.sons[i], ids, info)
of nkRecCase:
if (n.sons[0].kind != nkSym): internalError(info, "checkInitialized")
checkInitialized(n.sons[0], ids, info)
when false:
# XXX we cannot check here, as we don't know the branch!
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info)
else: internalError(info, "checkInitialized")
of nkSym:
if {tfNotNil, tfNeedsInit} * n.sym.typ.flags != {} and
n.sym.name.id notin ids:
message(info, errGenerated, "field not initialized: " & n.sym.name.s)
else: internalError(info, "checkInitialized")
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
var t = semTypeNode(c, n.sons[0], nil)
result = newNodeIT(nkObjConstr, n.info, t)
result.add n.sons[0]
t = skipTypes(t, {tyGenericInst, tyAlias})
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias})
if t.kind != tyObject:
localError(n.info, errGenerated, "object constructor needs an object type")
return
var objType = t
var ids = initIntSet()
for i in 1.. <n.len:
let it = n.sons[i]
if it.kind != nkExprColonExpr:
localError(n.info, errNamedExprExpected)
break
let id = considerQuotedIdent(it.sons[0])
if containsOrIncl(ids, id.id):
localError(it.info, errFieldInitTwice, id.s)
var e = semExprWithType(c, it.sons[1], flags*{efAllowDestructor})
var
check: PNode = nil
f: PSym
t = objType
while true:
check = nil
f = lookupInRecordAndBuildCheck(c, it, t.n, id, check)
if f != nil: break
if t.sons[0] == nil: break
t = skipTypes(t.sons[0], skipPtrs)
if f != nil and fieldVisible(c, f):
it.sons[0] = newSymNode(f)
e = fitNode(c, f.typ, e, it.info)
# small hack here in a nkObjConstr the ``nkExprColonExpr`` node can have
# 3 children the last being the field check
if check != nil:
check.sons[0] = it.sons[0]
it.add(check)
else:
localError(it.info, errUndeclaredFieldX, id.s)
it.sons[1] = e
result.add it
# XXX object field name check for 'case objects' if the kind is static?
if tfNeedsInit in objType.flags:
while true:
checkInitialized(objType.n, ids, n.info)
if objType.sons[0] == nil: break
objType = skipTypes(objType.sons[0], skipPtrs)
proc semBlock(c: PContext, n: PNode): PNode =
result = n
inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2)
openScope(c) # BUGFIX: label is in the scope of block!
if n.sons[0].kind != nkEmpty:
var labl = newSymG(skLabel, n.sons[0], c)
if sfGenSym notin labl.flags:
addDecl(c, labl)
n.sons[0] = newSymNode(labl, n.sons[0].info)
suggestSym(n.sons[0].info, labl, c.graph.usageSym)
styleCheckDef(labl)
n.sons[1] = semExpr(c, n.sons[1])
n.typ = n.sons[1].typ
if isEmptyType(n.typ): n.kind = nkBlockStmt
else: n.kind = nkBlockExpr
closeScope(c)
dec(c.p.nestedBlockCounter)
proc semExport(c: PContext, n: PNode): PNode =
var x = newNodeI(n.kind, n.info)
#let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len
for i in 0.. <n.len:
let a = n.sons[i]
var o: TOverloadIter
var s = initOverloadIter(o, c, a)
if s == nil:
localError(a.info, errGenerated, "cannot export: " & renderTree(a))
elif s.kind == skModule:
# forward everything from that module:
strTableAdd(c.module.tab, s)
x.add(newSymNode(s, a.info))
var ti: TTabIter
var it = initTabIter(ti, s.tab)
while it != nil:
if it.kind in ExportableSymKinds+{skModule}:
strTableAdd(c.module.tab, it)
it = nextIter(ti, s.tab)
else:
while s != nil:
if s.kind in ExportableSymKinds+{skModule}:
x.add(newSymNode(s, a.info))
strTableAdd(c.module.tab, s)
s = nextOverloadIter(o, c, a)
result = n
proc shouldBeBracketExpr(n: PNode): bool =
assert n.kind in nkCallKinds
let a = n.sons[0]
if a.kind in nkCallKinds:
let b = a[0]
if b.kind in nkSymChoices:
for i in 0..<b.len:
if b[i].sym.magic == mArrGet:
let be = newNodeI(nkBracketExpr, n.info)
for i in 1..<a.len: be.add(a[i])
n.sons[0] = be
return true
proc setGenericParams(c: PContext, n: PNode) =
for i in 1 .. <n.len:
n[i].typ = semTypeNode(c, n[i], nil)
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n
if gCmd == cmdIdeTools: suggestExpr(c, n)
if nfSem in n.flags: return
case n.kind
of nkIdent, nkAccQuoted:
let checks = if efNoEvaluateGeneric in flags: {checkUndeclared}
else: {checkUndeclared, checkModule, checkAmbiguity}
var s = qualifiedLookUp(c, n, checks)
if c.inTypeClass == 0: semCaptureSym(s, c.p.owner)
result = semSym(c, n, s, flags)
if s.kind in {skProc, skMethod, skConverter, skIterator}:
#performProcvarCheck(c, n, s)
result = symChoice(c, n, s, scClosed)
if result.kind == nkSym:
markIndirect(c, result.sym)
# if isGenericRoutine(result.sym):
# localError(n.info, errInstantiateXExplicitly, s.name.s)
of nkSym:
# because of the changed symbol binding, this does not mean that we
# don't have to check the symbol for semantics here again!
result = semSym(c, n, n.sym, flags)
of nkEmpty, nkNone, nkCommentStmt:
discard
of nkNilLit:
if result.typ == nil: result.typ = getSysType(tyNil)
of nkIntLit:
if result.typ == nil: setIntLitType(result)
of nkInt8Lit:
if result.typ == nil: result.typ = getSysType(tyInt8)
of nkInt16Lit:
if result.typ == nil: result.typ = getSysType(tyInt16)
of nkInt32Lit:
if result.typ == nil: result.typ = getSysType(tyInt32)
of nkInt64Lit:
if result.typ == nil: result.typ = getSysType(tyInt64)
of nkUIntLit:
if result.typ == nil: result.typ = getSysType(tyUInt)
of nkUInt8Lit:
if result.typ == nil: result.typ = getSysType(tyUInt8)
of nkUInt16Lit:
if result.typ == nil: result.typ = getSysType(tyUInt16)
of nkUInt32Lit:
if result.typ == nil: result.typ = getSysType(tyUInt32)
of nkUInt64Lit:
if result.typ == nil: result.typ = getSysType(tyUInt64)
of nkFloatLit:
if result.typ == nil: result.typ = getFloatLitType(result)
of nkFloat32Lit:
if result.typ == nil: result.typ = getSysType(tyFloat32)
of nkFloat64Lit:
if result.typ == nil: result.typ = getSysType(tyFloat64)
of nkFloat128Lit:
if result.typ == nil: result.typ = getSysType(tyFloat128)
of nkStrLit..nkTripleStrLit:
if result.typ == nil: result.typ = getSysType(tyString)
of nkCharLit:
if result.typ == nil: result.typ = getSysType(tyChar)
of nkDotExpr:
result = semFieldAccess(c, n, flags)
if result.kind == nkDotCall:
result.kind = nkCall
result = semExpr(c, result, flags)
of nkBind:
message(n.info, warnDeprecated, "bind")
result = semExpr(c, n.sons[0], flags)
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info)
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1)
#when defined(nimsuggest):
# if gIdeCmd == ideCon and gTrackPos == n.info: suggestExprNoCheck(c, n)
let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
var s = qualifiedLookUp(c, n.sons[0], mode)
if s != nil:
#if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr:
# pretty.checkUse(n.sons[0].sons[1].info, s)
case s.kind
of skMacro:
if sfImmediate notin s.flags:
result = semDirectOp(c, n, flags)
else:
result = semMacroExpr(c, n, n, s, flags)
of skTemplate:
if sfImmediate notin s.flags:
result = semDirectOp(c, n, flags)
else:
result = semTemplateExpr(c, n, s, flags)
of skType:
# XXX think about this more (``set`` procs)
if n.len == 2:
result = semConv(c, n)
elif n.len == 1:
result = semObjConstr(c, n, flags)
elif contains(c.ambiguousSymbols, s.id):
errorUseQualifier(c, n.info, s)
elif s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags)
of skProc, skMethod, skConverter, skIterator:
if s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags)
else:
#liMessage(n.info, warnUser, renderTree(n));
result = semIndirectOp(c, n, flags)
elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and
isSymChoice(n[0][0]):
# indirectOp can deal with explicit instantiations; the fixes
# the 'newSeq[T](x)' bug
setGenericParams(c, n.sons[0])
result = semDirectOp(c, n, flags)
elif isSymChoice(n.sons[0]) or nfDotField in n.flags:
result = semDirectOp(c, n, flags)
else:
result = semIndirectOp(c, n, flags)
of nkWhen:
if efWantStmt in flags:
result = semWhen(c, n, true)
else:
result = semWhen(c, n, false)
if result == n:
# This is a "when nimvm" stmt.
result = semWhen(c, n, true)
else:
result = semExpr(c, result, flags)
of nkBracketExpr:
checkMinSonsLen(n, 1)
result = semArrayAccess(c, n, flags)
of nkCurlyExpr:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags)
of nkPragmaExpr:
var
expr = n[0]
pragma = n[1]
pragmaName = considerQuotedIdent(pragma[0])
flags = flags
case whichKeyword(pragmaName)
of wExplain:
flags.incl efExplain
else:
# what other pragmas are allowed for expressions? `likely`, `unlikely`
invalidPragma(n)
result = semExpr(c, n[0], flags)
of nkPar:
case checkPar(n)
of paNone: result = errorNode(c, n)
of paTuplePositions:
var tupexp = semTuplePositionsConstr(c, n, flags)
if isTupleType(tupexp):
# reinterpret as type
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
result.typ = makeTypeDesc(c, typ)
else:
result = tupexp
of paTupleFields: result = semTupleFieldsConstr(c, n, flags)
of paSingle: result = semExpr(c, n.sons[0], flags)
of nkCurly: result = semSetConstr(c, n)
of nkBracket: result = semArrayConstr(c, n, flags)
of nkObjConstr: result = semObjConstr(c, n, flags)
of nkLambda: result = semLambda(c, n, flags)
of nkDo: result = semDo(c, n, flags)
of nkDerefExpr: result = semDeref(c, n)
of nkAddr:
result = n
checkSonsLen(n, 1)
result = semAddr(c, n.sons[0])
of nkHiddenAddr, nkHiddenDeref:
checkSonsLen(n, 1)
n.sons[0] = semExpr(c, n.sons[0], flags)
of nkCast: result = semCast(c, n)
of nkIfExpr, nkIfStmt: result = semIf(c, n)
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv:
checkSonsLen(n, 2)
considerGenSyms(c, n)
of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv:
checkSonsLen(n, 1)
considerGenSyms(c, n)
of nkChckRangeF, nkChckRange64, nkChckRange:
checkSonsLen(n, 3)
considerGenSyms(c, n)
of nkCheckedFieldExpr:
checkMinSonsLen(n, 2)
considerGenSyms(c, n)
of nkTableConstr:
result = semTableConstr(c, n)
of nkClosedSymChoice, nkOpenSymChoice:
# handling of sym choices is context dependent
# the node is left intact for now
discard
of nkStaticExpr:
result = semStaticExpr(c, n)
of nkAsgn: result = semAsgn(c, n)
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
of nkRaiseStmt: result = semRaise(c, n)
of nkVarSection: result = semVarOrLet(c, n, skVar)
of nkLetSection: result = semVarOrLet(c, n, skLet)
of nkConstSection: result = semConst(c, n)
of nkTypeSection: result = semTypeSection(c, n)
of nkDiscardStmt: result = semDiscard(c, n)
of nkWhileStmt: result = semWhile(c, n)
of nkTryStmt: result = semTry(c, n)
of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
of nkForStmt, nkParForStmt: result = semFor(c, n)
of nkCaseStmt: result = semCase(c, n)
of nkReturnStmt: result = semReturn(c, n)
of nkUsingStmt: result = semUsing(c, n)
of nkAsmStmt: result = semAsm(c, n)
of nkYieldStmt: result = semYield(c, n)
of nkPragma: pragma(c, c.p.owner, n, stmtPragmas)
of nkIteratorDef: result = semIterator(c, n)
of nkProcDef: result = semProc(c, n)
of nkMethodDef: result = semMethod(c, n)
of nkConverterDef: result = semConverterDef(c, n)
of nkMacroDef: result = semMacroDef(c, n)
of nkTemplateDef: result = semTemplateDef(c, n)
of nkImportStmt:
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "import")
result = evalImport(c, n)
of nkImportExceptStmt:
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "import")
result = evalImportExcept(c, n)
of nkFromStmt:
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "from")
result = evalFrom(c, n)
of nkIncludeStmt:
#if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "include")
result = evalInclude(c, n)
of nkExportStmt, nkExportExceptStmt:
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "export")
result = semExport(c, n)
of nkPragmaBlock:
result = semPragmaBlock(c, n)
of nkStaticStmt:
result = semStaticStmt(c, n)
of nkDefer:
n.sons[0] = semExpr(c, n.sons[0])
if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
localError(n.info, errGenerated, "'defer' takes a 'void' expression")
#localError(n.info, errGenerated, "'defer' not allowed in this context")
else:
localError(n.info, errInvalidExpressionX,
renderTree(n, {renderNoComments}))
if result != nil: incl(result.flags, nfSem)