Files
Nim/compiler/semexprs.nim
metagn 67ad1ae159 fix standalone explicit generic procs with unresolved arguments (#24404)
fixes issue described in https://forum.nim-lang.org/t/12579

In #24065 explicit generic parameter matching was made to fail matches
on arguments with unresolved types in generic contexts (the sigmatch
diff, following #24010), similar to what is done for regular calls since
#22029. However unlike regular calls, a failed match in a generic
context for a standalone explicit generic instantiation did not convert
the expression into one with `tyFromExpr` type, which means it would
error immediately given any unresolved parameter. This is now done to
fix the issue.

For explicit generic instantiations on single non-overloaded symbols, a
successful match is still instantiated. For multiple overloads (i.e.
symchoice), if any of the overloads fail the match, the entire
expression is considered untyped and any instantiations are not used, so
as to not void overloads that would match later. This means even
symchoices without unresolved arguments aren't instantiated, which may
be too restrictive, but it could also be too lenient and we might need
to make symchoice instantiations always untyped. The behavior for
symchoice is not sound anyway given it causes #9997 so this is something
to consider for a redesign.

Diff follows #24276.
2024-11-06 10:54:03 +01:00

3610 lines
138 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
when defined(nimCompilerStacktraceHints):
import std/stackframes
const
errExprXHasNoType = "expression '$1' has no type (or is ambiguous)"
errXExpectsTypeOrValue = "'$1' expects a type or value"
errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
errXStackEscape = "address of '$1' may not escape its stack frame"
errExprHasNoAddress = "expression has no address"
errCannotInterpretNodeX = "cannot evaluate '$1'"
errNamedExprExpected = "named expression expected"
errNamedExprNotAllowed = "named expression not allowed here"
errFieldInitTwice = "field initialized twice: '$1'"
errUndeclaredFieldX = "undeclared field: '$1'"
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
rememberExpansion(c, n.info, s)
let info = getCallLineInfo(n)
markUsed(c, info, s)
onUse(info, s)
# Note: This is n.info on purpose. It prevents template from creating an info
# context when called from an another template
pushInfoContext(c.config, n.info, s.detailedInfo)
result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache,
c.templInstCounter, c.idgen, efFromHlo in flags)
if efNoSemCheck notin flags:
result = semAfterMacroCall(c, n, result, s, flags, expectedType)
popInfoContext(c.config)
# XXX: A more elaborate line info rewrite might be needed
result.info = info
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
template rejectEmptyNode(n: PNode) =
# No matter what a nkEmpty node is not what we want here
if n.kind == nkEmpty: illFormedAst(n, c.config)
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
rejectEmptyNode(n)
# same as 'semExprWithType' but doesn't check for proc vars
result = semExpr(c, n, flags + {efOperand, efAllowSymChoice})
if result.typ != nil:
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ() = newTypeS(tyVoid, c)
else:
localError(c.config, n.info, errExprXHasNoType %
renderTree(result, {renderNoComments}))
result.typ() = errorType(c)
proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = nil): PNode =
rejectEmptyNode(n)
result = semExpr(c, n, flags+{efWantValue}, expectedType)
let
isEmpty = result.kind == nkEmpty
isTypeError = result.typ != nil and result.typ.kind == tyError
if isEmpty or isTypeError:
# bug #12741, redundant error messages are the lesser evil here:
localError(c.config, n.info, errExprXHasNoType %
renderTree(result, {renderNoComments}))
if isEmpty:
# do not produce another redundant error message:
result = errorNode(c, n)
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
result = semExprCheck(c, n, flags-{efTypeAllowed}, expectedType)
if result.typ == nil and efInTypeof in flags:
result.typ() = c.voidType
elif result.typ == nil or result.typ == c.enforceVoidContext:
localError(c.config, n.info, errExprXHasNoType %
renderTree(result, {renderNoComments}))
result.typ() = errorType(c)
elif result.typ.kind == tyError:
# associates the type error to the current owner
result.typ() = errorType(c)
elif efTypeAllowed in flags and result.typ.kind == tyProc and
hasUnresolvedParams(result, {}):
# mirrored with semOperand but only on efTypeAllowed
let owner = result.typ.owner
let err =
# consistent error message with evaltempl/semMacroExpr
if owner != nil and owner.kind in {skTemplate, skMacro}:
errMissingGenericParamsForTemplate % n.renderTree
else:
errProcHasNoConcreteType % n.renderTree
localError(c.config, n.info, err)
result.typ() = errorType(c)
else:
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExprCheck(c, n, flags)
if result.typ == nil:
localError(c.config, n.info, errExprXHasNoType %
renderTree(result, {renderNoComments}))
result.typ() = errorType(c)
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = symChoice(c, n, s, scClosed)
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode
proc isSymChoice(n: PNode): bool {.inline.} =
result = n.kind in nkSymChoices
proc resolveSymChoice(c: PContext, n: var PNode, flags: TExprFlags = {}, expectedType: PType = nil) =
## Attempts to resolve a symchoice `n`, `n` remains a symchoice if
## it cannot be resolved (this is the case even when `n.len == 1`).
if expectedType != nil:
# resolve from type inference, see paramTypesMatch
n = fitNode(c, expectedType, n, n.info)
if isSymChoice(n) and efAllowSymChoice notin flags:
# some contexts might want sym choices preserved for later disambiguation
# in general though they are ambiguous
let first = n[0].sym
var foundSym: PSym = nil
if first.kind == skEnumField and
not isAmbiguous(c, first.name, {skEnumField}, foundSym) and
foundSym == first:
# choose the first resolved enum field, i.e. the latest in scope
# to mirror behavior before overloadable enums
n = n[0]
proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType,
warnDisabled = false): PNode =
## sem the child of an `nkOpenSym` node, that is, captured symbols that can be
## replaced by newly injected symbols in generics. `s` must be the captured
## symbol if the original node is an `nkSym` node; and `nil` if it is an
## `nkOpenSymChoice`, in which case only non-overloadable injected symbols
## will be considered.
let isSym = n.kind == nkSym
let ident = n.getPIdent
assert ident != nil
let id = newIdentNode(ident, n.info)
c.isAmbiguous = false
let s2 = qualifiedLookUp(c, id, {})
# for `nkSym`, the first found symbol being different and unambiguous is
# enough to replace the original
# for `nkOpenSymChoice`, the first found symbol must be non-overloadable,
# since otherwise we have to use regular `nkOpenSymChoice` functionality
# but of the overloadable sym kinds, semExpr does not handle skModule, skMacro, skTemplate
# as overloaded in the case where `nkIdent` finds them first
if s2 != nil and not c.isAmbiguous and
((isSym and s2 != n.sym) or
(not isSym and s2.kind notin OverloadableSyms-{skModule, skMacro, skTemplate})):
# only consider symbols defined under current proc:
var o = s2.owner
while o != nil:
if o == c.p.owner:
if not warnDisabled:
result = semExpr(c, id, flags, expectedType)
return
else:
var msg =
"a new symbol '" & ident.s & "' has been injected during " &
# msgContext should show what is being instantiated:
"template or generic instantiation, however "
if isSym:
msg.add(
getSymRepr(c.config, n.sym) & " captured at " &
"the proc declaration will be used instead; " &
"either enable --experimental:openSym to use the injected symbol, " &
"or `bind` this captured symbol explicitly")
else:
msg.add(
"overloads of " & ident.s & " will be used instead; " &
"either enable --experimental:openSym to use the injected symbol, " &
"or `bind` this symbol explicitly")
message(c.config, n.info, warnIgnoredSymbolInjection, msg)
break
o = o.owner
# nothing found
n.flags.excl nfDisabledOpenSym
if not warnDisabled and isSym:
result = semExpr(c, n, flags, expectedType)
else:
result = nil
if not isSym:
# set symchoice node type back to None
n.typ() = newTypeS(tyNone, c)
proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
if n.kind == nkOpenSymChoice:
result = semOpenSym(c, n, flags, expectedType,
warnDisabled = nfDisabledOpenSym in n.flags and
genericsOpenSym notin c.features)
if result != nil:
return
result = n
resolveSymChoice(c, result, flags, expectedType)
if isSymChoice(result) and result.len == 1:
# resolveSymChoice can leave 1 sym
result = result[0]
if isSymChoice(result) and efAllowSymChoice notin flags:
var err = "ambiguous identifier: '" & result[0].sym.name.s &
"' -- use one of the following:\n"
for child in n:
let candidate = child.sym
err.add " " & candidate.owner.name.s & "." & candidate.name.s
err.add ": " & typeToString(candidate.typ) & "\n"
localError(c.config, n.info, err)
n.typ() = errorType(c)
result = n
if result.kind == nkSym:
result = semSym(c, result, result.sym, flags)
proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} =
result = copyTree(s.astdef)
if result.isNil:
localError(c.config, 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,
convNotInRange
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, targetTyp: PType, src: PNode): TConvStatus =
let srcTyp = src.typ.skipTypes({tyStatic})
result = convOK
if sameType(targetTyp, srcTyp) and targetTyp.sym == srcTyp.sym:
# don't annoy conversions that may be needed on another processor:
if targetTyp.kind notin IntegralTypes+{tyRange}:
result = convNotNeedeed
return
var d = skipTypes(targetTyp, abstractVar)
var s = srcTyp
if s.kind in tyUserTypeClasses and s.isResolvedUserTypeClass:
s = s.last
s = skipTypes(s, abstractVar-{tyTypeDesc, tyOwned})
if s.kind == tyOwned and d.kind != tyOwned:
s = s.skipModifier
var pointers = 0
while (d != nil) and (d.kind in {tyPtr, tyRef, tyOwned}):
if s.kind == tyOwned and d.kind != tyOwned:
s = s.skipModifier
elif d.kind != s.kind:
break
else:
d = d.elementType
s = s.elementType
inc pointers
let targetBaseTyp = skipTypes(targetTyp, abstractVarRange)
let srcBaseTyp = skipTypes(srcTyp, abstractVarRange-{tyTypeDesc})
if d == nil:
result = convNotLegal
elif d.skipTypes(abstractInst).kind == tyObject and s.skipTypes(abstractInst).kind == tyObject:
result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers)
elif (targetBaseTyp.kind in IntegralTypes) and
(srcBaseTyp.kind in IntegralTypes):
if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum and
not sameType(targetTyp, srcBaseTyp):
message(c.config, src.info, warnSuspiciousEnumConv, "suspicious code: enum to enum conversion")
# `elif` would be incorrect here
if targetTyp.kind == tyBool:
discard "convOk"
elif targetTyp.isOrdinalType:
if src.kind in nkCharLit..nkUInt64Lit and
src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp) and
targetTyp.kind notin {tyUInt..tyUInt64}:
result = convNotInRange
elif src.kind in nkFloatLit..nkFloat64Lit and
(classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or
src.floatVal.int64 notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp)):
result = convNotInRange
elif targetBaseTyp.kind in tyFloat..tyFloat64:
if src.kind in nkFloatLit..nkFloat64Lit and
not floatRangeCheck(src.floatVal, targetTyp):
result = convNotInRange
elif src.kind in nkCharLit..nkUInt64Lit and
not floatRangeCheck(src.intVal.float, targetTyp):
result = convNotInRange
else:
# we use d, s here to speed up that operation a bit:
if d.kind == tyFromExpr:
result = convNotLegal
return
case cmpTypes(c, d, s)
of isNone, isGeneric:
if not compareTypes(targetTyp.skipTypes(abstractVar), srcTyp.skipTypes({tyOwned}), dcEqIgnoreDistinct):
result = convNotLegal
else:
discard
proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
## Checks whether the source type can be cast to the destination type.
## Casting is very unrestrictive; casts are allowed as long as
## dst.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
if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass:
return false
let conf = c.config
if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
let d = skipTypes(dst, abstractInst)
let s = skipTypes(src, abstractInst)
if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal:
return false
elif d.kind in IntegralTypes and s.kind in {tyString, tySequence}:
return false
var dstSize, srcSize: BiggestInt
dstSize = computeSize(conf, dst)
srcSize = computeSize(conf, src)
if dstSize == -3 or srcSize == -3: # szUnknownSize
# The Nim compiler can't detect if it's legal or not.
# Just assume the programmer knows what he is doing.
return true
if dstSize < 0:
return false
elif srcSize < 0:
return false
elif typeAllowed(dst, skParam, c, {taIsCastable}) != nil:
return false
elif dst.kind == tyProc and dst.callConv == ccClosure:
return src.kind == tyProc and src.callConv == ccClosure
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:
return dst.size <= conf.target.ptrSize
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 isOwnedSym(c: PContext; n: PNode): bool =
let s = qualifiedLookUp(c, n, {})
result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned"
proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode =
if n.len != 2:
localError(c.config, n.info, "a type conversion takes exactly one argument")
return n
result = newNodeI(nkConv, n.info)
var targetType = semTypeNode(c, n[0], nil)
case targetType.skipTypes({tyDistinct}).kind
of tyTypeDesc:
internalAssert c.config, targetType.len > 0
if targetType.base.kind == tyNone:
return semTypeOf(c, n)
else:
targetType = targetType.base
of tyStatic:
var evaluated = semStaticExpr(c, n[1], expectedType)
if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc:
result = n
result.typ() = c.makeTypeDesc semStaticType(c, evaluated, nil)
return
elif targetType.base.kind == tyNone:
return evaluated
else:
targetType = targetType.base
of tyAnything, tyUntyped, tyTyped:
localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType))
else: discard
maybeLiftType(targetType, c, n[0].info)
if targetType.kind in {tySink, tyLent} or isOwnedSym(c, n[0]):
let baseType = semTypeNode(c, n[1], nil).skipTypes({tyTypeDesc})
let t = newTypeS(targetType.kind, c, baseType)
if targetType.kind == tyOwned:
t.flags.incl tfHasOwned
result = newNodeI(nkType, n.info)
result.typ() = makeTypeDesc(c, t)
return
result.add copyTree(n[0])
# special case to make MyObject(x = 3) produce a nicer error message:
if n[1].kind == nkExprEqExpr and
targetType.skipTypes(abstractPtrs).kind == tyObject:
localError(c.config, n.info, "object construction uses ':', not '='")
var op = semExprWithType(c, n[1], flags * {efDetermineType} + {efAllowSymChoice})
if isSymChoice(op) and op[0].sym.kind notin routineKinds:
# T(foo) disambiguation syntax only allowed for routines
op = semSymChoice(c, op)
if targetType.kind != tyGenericParam and targetType.isMetaType:
let final = inferWithMetatype(c, targetType, op, true)
result.add final
result.typ() = final.typ
return
result.typ() = targetType
# XXX op is overwritten later on, this is likely added too early
# here or needs to be overwritten too then.
result.add op
if targetType.kind == tyGenericParam or
(op.typ != nil and op.typ.kind == tyFromExpr and c.inGenericContext > 0):
# expression is compiled early in a generic body
result.typ() = makeTypeFromExpr(c, copyTree(result))
return result
if not isSymChoice(op):
let status = checkConvertible(c, result.typ, op)
case status
of convOK:
# handle SomeProcType(SomeGenericProc)
if op.kind == nkSym and op.sym.isGenericRoutine:
result[1] = fitNode(c, result.typ, result[1], result.info)
elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple:
op = fitNode(c, targetType, op, result.info)
of convNotNeedeed:
if efNoSem2Check notin flags:
message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
of convNotLegal:
result = fitNode(c, result.typ, result[1], result.info)
if result == nil:
localError(c.config, n.info, "illegal conversion from '$1' to '$2'" %
[op.typ.typeToString, result.typ.typeToString])
of convNotInRange:
let value =
if op.kind in {nkCharLit..nkUInt64Lit}: $op.getInt else: $op.getFloat
localError(c.config, n.info, errGenerated, value & " can't be converted to " &
result.typ.typeToString)
else:
for i in 0..<op.len:
let it = op[i]
let status = checkConvertible(c, result.typ, it)
if status in {convOK, convNotNeedeed}:
markUsed(c, n.info, it.sym)
onUse(n.info, it.sym)
markIndirect(c, it.sym)
return it
errorUseQualifier(c, n.info, op[0].sym)
proc semCast(c: PContext, n: PNode): PNode =
## Semantically analyze a casting ("cast[type](param)")
checkSonsLen(n, 2, c.config)
let targetType = semTypeNode(c, n[0], nil)
let castedExpr = semExprWithType(c, n[1])
if castedExpr.kind == nkClosedSymChoice:
errorUseQualifier(c, n[1].info, castedExpr)
if targetType == nil:
localError(c.config, n.info, "Invalid usage of cast, cast requires a type to convert to, e.g., cast[int](0d).")
if tfHasMeta in targetType.flags:
localError(c.config, n[0].info, "cannot cast to a non concrete type: '$1'" % $targetType)
if not isCastable(c, targetType, castedExpr.typ, n.info):
localError(c.config, n.info, "expression cannot be cast to '$1'" % $targetType)
result = newNodeI(nkCast, n.info)
result.typ() = targetType
result.add copyTree(n[0])
result.add castedExpr
proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
const
opToStr: array[mLow..mHigh, string] = ["low", "high"]
if n.len != 2:
localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m])
else:
n[1] = semExprWithType(c, n[1], {efDetermineType})
var typ = skipTypes(n[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst})
case typ.kind
of tySequence, tyString, tyCstring, tyOpenArray, tyVarargs:
n.typ() = getSysType(c.graph, n.info, tyInt)
of tyArray:
n.typ() = typ.indexType
if n.typ.kind == tyRange and emptyRange(n.typ.n[0], n.typ.n[1]): #Invalid range
n.typ() = getSysType(c.graph, n.info, tyInt)
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt..tyUInt64, tyFloat..tyFloat64:
n.typ() = n[1].typ.skipTypes({tyTypeDesc})
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(c.config, n.info, "invalid argument for: " & opToStr[m])
result = n
proc fixupStaticType(c: PContext, n: PNode) =
# This proc can be applied to evaluated expressions to assign
# them a static type.
#
# XXX: with implicit static, this should not be necessary,
# because the output type of operations such as `semConstExpr`
# should be a static type (as well as the type of any other
# expression that can be implicitly evaluated). For now, we
# apply this measure only in code that is enlightened to work
# with static types.
if n.typ.kind != tyStatic:
n.typ() = newTypeS(tyStatic, c, n.typ)
n.typ.n = n # XXX: cycles like the one here look dangerous.
# Consider using `n.copyTree`
proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
internalAssert c.config,
n.len == 3 and
n[1].typ != nil and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
var
res = false
t1 = n[1].typ
t2 = n[2].typ
if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc:
t1 = t1.base
if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize
of "closure":
let t = skipTypes(t1, abstractRange)
res = t.kind == tyProc and
t.callConv == ccClosure
of "iterator":
# holdover from when `is iterator` didn't work
let t = skipTypes(t1, abstractRange)
res = t.kind == tyProc and
t.callConv == ccClosure and
tfIterator in t.flags
else:
res = false
else:
if t1.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}).kind != tyGenericBody:
maybeLiftType(t2, c, n.info)
else:
#[
for this case:
type Foo = object[T]
Foo is Foo
]#
discard
var m = newCandidate(c, t2)
if efExplain in flags:
m.diagnostics = @[]
m.diagnosticsEnabled = true
res = typeRel(m, t2, t1) >= isSubtype # isNone
# `res = sameType(t1, t2)` would be wrong, e.g. for `int is (int|float)`
result = newIntNode(nkIntLit, ord(res))
result.typ() = n.typ
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
if n.len != 3 or n[2].kind == nkEmpty:
localError(c.config, n.info, "'is' operator takes 2 arguments")
return errorNode(c, n)
let boolType = getSysType(c.graph, n.info, tyBool)
result = n
n.typ() = boolType
var liftLhs = true
n[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
let t2 = semTypeNode(c, n[2], nil)
n[2] = newNodeIT(nkType, n[2].info, t2)
if t2.kind == tyStatic:
let evaluated = tryConstExpr(c, n[1])
if evaluated != nil:
c.fixupStaticType(evaluated)
n[1] = evaluated
else:
result = newIntNode(nkIntLit, 0)
result.typ() = boolType
return
elif t2.kind == tyTypeDesc and
(t2.base.kind == tyNone or tfExplicit in t2.flags):
# When the right-hand side is an explicit type, we must
# not allow regular values to be matched against the type:
liftLhs = false
else:
n[2] = semExpr(c, n[2])
var lhsType = n[1].typ
if lhsType.kind != tyTypeDesc:
if liftLhs:
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
lhsType = n[1].typ
else:
if c.inGenericContext > 0 and lhsType.base.containsUnresolvedType:
# BUGFIX: don't evaluate this too early: ``T is void``
return
result = isOpImpl(c, n, flags)
proc semOpAux(c: PContext, n: PNode) =
const flags = {efDetermineType, efAllowSymChoice}
for i in 1..<n.len:
var a = n[i]
if a.kind == nkExprEqExpr and a.len == 2:
let info = a[0].info
a[0] = newIdentNode(considerQuotedIdent(c, a[0], a), info)
a[1] = semExprWithType(c, a[1], flags)
a.typ() = a[1].typ
else:
n[i] = semExprWithType(c, a, flags)
proc overloadedCallOpr(c: PContext, n: PNode): PNode =
# quick check if there is *any* () operator overloaded:
var par = getIdent(c.cache, "()")
var amb = false
if searchInScopes(c, par, amb) == nil:
result = nil
else:
result = newNodeI(nkCall, n.info)
result.add newIdentNode(par, n.info)
for i in 0..<n.len: result.add n[i]
result = semExpr(c, result, flags = {efNoUndeclared})
proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
case n.kind
of nkCurly:
for i in 0..<n.len:
if n[i].kind == nkRange:
changeType(c, n[i][0], elemType(newType), check)
changeType(c, n[i][1], elemType(newType), check)
else:
changeType(c, n[i], elemType(newType), check)
of nkBracket:
for i in 0..<n.len:
changeType(c, n[i], elemType(newType), check)
of nkPar, nkTupleConstr:
let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
if tup.kind != tyTuple:
if tup.kind == tyObject: return
globalError(c.config, n.info, "no tuple type for constructor")
elif n.len > 0 and n[0].kind == nkExprColonExpr:
# named tuple?
for i in 0..<n.len:
var m = n[i][0]
if m.kind != nkSym:
globalError(c.config, m.info, "invalid tuple constructor")
return
if tup.n != nil:
var f = getSymFromList(tup.n, m.sym.name)
if f == nil:
globalError(c.config, m.info, "unknown identifier: " & m.sym.name.s)
return
changeType(c, n[i][1], f.typ, check)
else:
changeType(c, n[i][1], tup[i], check)
else:
for i in 0..<n.len:
changeType(c, n[i], tup[i], check)
when false:
var m = n[i]
var a = newNodeIT(nkExprColonExpr, m.info, newType[i])
a.add newSymNode(newType.n[i].sym)
a.add m
changeType(m, tup[i], check)
of nkCharLit..nkUInt64Lit:
if check and n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType):
let value = n.intVal
if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
localError(c.config, n.info, "cannot convert " & $value &
" to " & typeNameAndDesc(newType))
of nkFloatLit..nkFloat64Lit:
if check and not floatRangeCheck(n.floatVal, newType):
localError(c.config, n.info, errFloatToString % [$n.floatVal, typeNameAndDesc(newType)])
of nkSym:
if check and n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType):
let value = n.sym.position
if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
localError(c.config, n.info, "cannot convert '" & n.sym.name.s &
"' to '" & typeNameAndDesc(newType) & "'")
else: discard
n.typ() = newType
proc arrayConstrType(c: PContext, n: PNode): PType =
var typ = newTypeS(tyArray, c)
rawAddSon(typ, nil) # index type
if n.len == 0:
rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
else:
var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
addSonSkipIntLit(typ, t, c.idgen)
typ.setIndexType makeRangeType(c, 0, n.len - 1, n.info)
result = typ
proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = newNodeI(nkBracket, n.info)
# nkBracket nodes can also be produced by the VM as seq constant nodes
# in which case, we cannot produce a new array type for the node,
# as this might lose type info even when the node has array type
let constructType = n.typ.isNil
var expectedElementType, expectedIndexType: PType = nil
var expectedBase: PType = nil
if constructType:
result.typ() = newTypeS(tyArray, c)
rawAddSon(result.typ, nil) # index type
if expectedType != nil:
expectedBase = expectedType.skipTypes(abstractRange-{tyDistinct})
else:
result.typ() = n.typ
expectedBase = n.typ.skipTypes(abstractRange) # include tyDistinct this time
if expectedBase != nil:
case expectedBase.kind
of tyArray:
expectedIndexType = expectedBase[0]
expectedElementType = expectedBase[1]
of tyOpenArray, tySequence:
# typed bracket expressions can also have seq type
expectedElementType = expectedBase[0]
else: discard
var
firstIndex, lastIndex: Int128 = Zero
indexType = getSysType(c.graph, n.info, tyInt)
lastValidIndex = lastOrd(c.config, indexType)
if n.len == 0:
if constructType:
rawAddSon(result.typ,
if expectedElementType != nil and
typeAllowed(expectedElementType, skLet, c) == nil:
expectedElementType
else:
newTypeS(tyEmpty, c)) # needs an empty basetype!
lastIndex = toInt128(-1)
else:
var x = n[0]
if x.kind == nkExprColonExpr and x.len == 2:
var idx = semConstExpr(c, x[0], expectedIndexType)
if not isOrdinalType(idx.typ):
localError(c.config, idx.info, "expected ordinal value for array " &
"index, got '$1'" % renderTree(idx))
else:
firstIndex = getOrdValue(idx)
lastIndex = firstIndex
indexType = idx.typ
lastValidIndex = lastOrd(c.config, indexType)
x = x[1]
let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
var typ: PType
if constructType:
typ = yy.typ
if expectedElementType == nil:
expectedElementType = typ
else:
typ = expectedElementType
result.add yy
#var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal})
for i in 1..<n.len:
if lastIndex == lastValidIndex:
let validIndex = makeRangeType(c, toInt64(firstIndex), toInt64(lastValidIndex), n.info,
indexType)
localError(c.config, n.info, "size of array exceeds range of index " &
"type '$1' by $2 elements" % [typeToString(validIndex), $(n.len-i)])
x = n[i]
if x.kind == nkExprColonExpr and x.len == 2:
var idx = semConstExpr(c, x[0], indexType)
idx = fitNode(c, indexType, idx, x.info)
if lastIndex+1 != getOrdValue(idx):
localError(c.config, x.info, "invalid order in array constructor")
x = x[1]
let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
result.add xx
if constructType:
typ = commonType(c, typ, xx.typ)
#n[i] = semExprWithType(c, x, {})
#result.add fitNode(c, typ, n[i])
inc(lastIndex)
if constructType:
addSonSkipIntLit(result.typ, typ, c.idgen)
for i in 0..<result.len:
result[i] = fitNode(c, typ, result[i], result[i].info)
if constructType:
result.typ.setIndexType(
makeRangeType(c,
toInt64(firstIndex), toInt64(lastIndex),
n.info, indexType))
proc fixAbstractType(c: PContext, n: PNode) =
for i in 1..<n.len:
let it = n[i]
if it == nil:
localError(c.config, n.info, "'$1' has nil child at index $2" % [renderTree(n, {renderNoComments}), $i])
return
# 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[1].typ, abstractVar).kind in
{tyNil, tyTuple, tySet} or it[1].isArrayConstr:
var s = skipTypes(it.typ, abstractVar + tyUserTypeClasses)
if s.kind != tyUntyped:
changeType(c, it[1], s, check=true)
n[i] = it[1]
proc isAssignable(c: PContext, n: PNode): TAssignableResult =
result = parampatterns.isAssignable(c.p.owner, n)
proc isUnresolvedSym(s: PSym): bool =
result = s.kind == skGenericParam
if not result and s.typ != nil:
result = tfInferrableStatic in s.typ.flags or
(s.kind == skParam and (s.typ.isMetaType or sfTemplateParam in s.flags)) or
(s.kind == skType and
s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
# Checks whether an expression depends on generic parameters that
# don't have bound values yet. E.g. this could happen in situations
# such as:
# type Slot[T] = array[T.size, byte]
# proc foo[T](x: default(T))
#
# Both static parameter and type parameters can be unresolved.
case n.kind
of nkSym:
return isUnresolvedSym(n.sym)
of nkIdent, nkAccQuoted:
let ident = considerQuotedIdent(c, n)
var amb = false
let sym = searchInScopes(c, ident, amb)
if sym != nil:
return isUnresolvedSym(sym)
else:
return false
else:
for i in 0..<n.safeLen:
if hasUnresolvedArgs(c, n[i]): return true
return false
proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or
sfCompileToCpp in c.module.flags):
checkSonsLen(n, 1, c.config)
result = n[0]
else:
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
result.add n
let aa = isAssignable(c, n)
let sym = getRoot(n)
if aa notin {arLValue, arLocalLValue}:
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
elif strictDefs in c.features and aa == arAddressableConst and
sym != nil and sym.kind == skLet and isOutParam:
discard "allow let varaibles to be passed to out parameters"
else:
localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
result = n
case n.kind
of nkSym:
# n.sym.typ can be nil in 'check' mode ...
if n.sym.typ != nil and
skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n.sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
of nkDotExpr:
checkSonsLen(n, 2, c.config)
if n[1].kind != nkSym:
internalError(c.config, n.info, "analyseIfAddressTaken")
return
if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n[1].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
of nkBracketExpr:
checkMinSonsLen(n, 1, c.config)
if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
else:
result = newHiddenAddrTaken(c, n, isOutParam)
proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) =
checkMinSonsLen(n, 1, c.config)
if n[0].typ == nil:
# n[0] might be erroring node in nimsuggest
return
const
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove,
mWasMoved}
template checkIfConverterCalled(c: PContext, n: PNode) =
## Checks if there is a converter call which wouldn't be checked otherwise
# Call can sometimes be wrapped in a deref
let node = if n.kind == nkHiddenDeref: n[0] else: n
if node.kind == nkHiddenCallConv:
analyseIfAddressTakenInCall(c, node, true)
# get the real type of the callee
# it may be a proc var with a generic alias type, so we skip over them
var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams:
# BUGFIX: check for L-Value still needs to be done for the arguments!
# note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
for i in 1..<n.len:
if i < t.len and t[i] != nil and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
let it = n[i]
let aa = isAssignable(c, it)
if aa notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr:
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
else:
localError(c.config, it.info, errVarForOutParamNeededX % $it)
# Make sure to still check arguments for converters
c.checkIfConverterCalled(n[i])
# bug #5113: disallow newSeq(result) where result is a 'var T':
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
var arg = n[1] #.skipAddr
if arg.kind == nkHiddenDeref: arg = arg[0]
if arg.kind == nkSym and arg.sym.kind == skResult and
arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments}))
return
for i in 1..<n.len:
let n = if n.kind == nkHiddenDeref: n[0] else: n
c.checkIfConverterCalled(n[i])
if i < t.len and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
# Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet.
# So we need to make sure we are checking them still when in a converter call
if n[i].kind != nkHiddenAddr or isConverter:
n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc})))
include semmagic
proc evalAtCompileTime(c: PContext, n: PNode): PNode =
result = n
if n.kind notin nkCallKinds or n[0].kind != nkSym: return
var callee = n[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[0])
var allConst = true
for i in 1..<n.len:
var a = getConstExpr(c.module, n[i], c.idgen, c.graph)
if a == nil:
allConst = false
a = n[i]
if a.kind == nkHiddenStdConv: a = a[1]
call.add(a)
if allConst:
result = semfold.getConstExpr(c.module, call, c.idgen, c.graph)
if result.isNil: result = n
else: return result
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() = newTypeS(tyStatic, c, n.typ)
n.typ.flags.incl tfUnresolved
# optimization pass: not necessary for correctness of the semantic pass
if (callee.kind == skConst or
{sfNoSideEffect, sfCompileTime} * callee.flags != {} and
{sfForward, sfImportc} * callee.flags == {}) and n.typ != nil:
if callee.kind != skConst and
sfCompileTime notin callee.flags and
optImplicitStatic notin c.config.options: return
if callee.magic notin ctfeWhitelist: return
if callee.kind notin {skProc, skFunc, skConverter, skConst} or
callee.isGenericRoutineStrict:
return
if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return
var call = newNodeIT(nkCall, n.info, n.typ)
call.add(n[0])
for i in 1..<n.len:
let a = getConstExpr(c.module, n[i], c.idgen, c.graph)
if a == nil: return n
call.add(a)
#echo "NOW evaluating at compile time: ", call.renderTree
if c.inStaticContext == 0 or sfNoSideEffect in callee.flags:
if sfCompileTime in callee.flags:
result = evalStaticExpr(c.module, c.idgen, c.graph, call, c.p.owner)
if result.isNil:
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
else: result = fixupTypeAfterEval(c, result, n)
else:
result = evalConstExpr(c.module, c.idgen, c.graph, call)
if result.isNil: result = n
else: result = fixupTypeAfterEval(c, result, n)
else:
result = n
#if result != n:
# echo "SUCCESS evaluated at compile time: ", call.renderTree
proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
inc c.inStaticContext
openScope(c)
let a = semExprWithType(c, n, expectedType = expectedType)
closeScope(c)
dec c.inStaticContext
if a.findUnresolvedStatic != nil: return a
result = evalStaticExpr(c.module, c.idgen, c.graph, a, c.p.owner)
if result.isNil:
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
result = c.graph.emptyNode
else:
result = fixupTypeAfterEval(c, result, a)
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags; expectedType: PType = nil): PNode =
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
# consider: 'for x in pReturningArray()' --> we don't want the restriction
# to 'skIterator' anymore; skIterator is preferred in sigmatch already
# for typeof support.
# for ``typeof(countup(1,3))``, see ``tests/ttoseq``.
result = semOverloadedCall(c, n, nOrig,
{skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags, expectedType)
else:
result = semOverloadedCall(c, n, nOrig,
{skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags, expectedType)
if result != nil:
if result[0].kind != nkSym:
if not (c.inGenericContext > 0): # see generic context check in semOverloadedCall
internalError(c.config, "semOverloadedCallAnalyseEffects")
return
let callee = result[0].sym
case callee.kind
of skMacro, skTemplate: discard
else:
if callee.kind == skIterator and callee.id == c.p.owner.id and
not isClosureIterator(c.p.owner.typ):
localError(c.config, n.info, errRecursiveDependencyIteratorX % callee.name.s)
# error correction, prevents endless for loop elimination in transf.
# See bug #2051:
result[0] = newSymNode(errorSym(c, n))
elif callee.kind == skIterator:
if efWantIterable in flags:
let typ = newTypeS(tyIterable, c)
rawAddSon(typ, result.typ)
result.typ() = typ
proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
t: PType): TCandidate =
result = initCandidate(c, t)
matches(c, n, nOrig, result)
proc finishOperand(c: PContext, a: PNode): PNode =
if a.typ.isNil:
result = c.semOperand(c, a, {efDetermineType})
else:
result = a
# XXX tyGenericInst here?
if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
#and tfUnresolved in result.typ.flags:
let owner = result.typ.owner
let err =
# consistent error message with evaltempl/semMacroExpr
if owner != nil and owner.kind in {skTemplate, skMacro}:
errMissingGenericParamsForTemplate % a.renderTree
else:
errProcHasNoConcreteType % a.renderTree
localError(c.config, a.info, err)
considerGenSyms(c, result)
proc semFinishOperands(c: PContext; n: PNode; isBracketExpr = false) =
# this needs to be called to ensure that after overloading resolution every
# argument has been sem'checked
# skip the first argument for operands of `[]` since it may be an unresolved
# generic proc, which is handled in semMagic
let start = 1 + ord(isBracketExpr)
for i in start..<n.len:
n[i] = finishOperand(c, n[i])
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
return errorNode(c, n)
if n.typ != nil and n.typ.kind == tyFromExpr and c.inGenericContext > 0:
return n
result = n
when defined(nimsuggest):
if c.config.expandProgress:
if c.config.expandLevels == 0:
return n
else:
c.config.expandLevels -= 1
let callee = result[0].sym
case callee.kind
of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
else:
semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut})
activate(c, result)
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
if callee.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags, expectedType)
when false:
if result.typ != nil and
not (result.typ.kind == tySequence and result.elementType.kind == tyEmpty):
liftTypeBoundOps(c, result.typ, n.info)
#result = patchResolvedTypeBoundOp(c, result)
if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone):
# don't fold calls in concepts and typeof
result = evalAtCompileTime(c, result)
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = nil
checkMinSonsLen(n, 1, c.config)
var prc = n[0]
if n[0].kind == nkDotExpr:
checkSonsLen(n[0], 2, c.config)
let n0 = semFieldAccess(c, n[0], {efIsDotCall})
if n0.kind == nkDotCall:
# it is a static call!
result = n0
result.transitionSonsKind(nkCall)
result.flags.incl nfExplicitCall
for i in 1..<n.len: result.add n[i]
return semExpr(c, result, flags, expectedType)
elif n0.typ.kind == tyFromExpr and c.inGenericContext > 0:
# don't make assumptions, entire expression needs to be tyFromExpr
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
else:
n[0] = n0
else:
n[0] = semExpr(c, n[0], {efInCall, efAllowSymChoice})
let t = n[0].typ
if t != nil and t.kind in {tyVar, tyLent}:
n[0] = newDeref(n[0])
elif isSymChoice(n[0]) and nfDotField notin n.flags:
# overloaded generic procs e.g. newSeq[int] can end up here
return semDirectOp(c, n, flags, expectedType)
var t: PType = nil
if n[0].typ != nil:
t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct})
if t != nil and t.kind == tyTypeDesc:
if n.len == 1: return semObjConstr(c, n, flags, expectedType)
return semConv(c, n, flags)
let nOrig = n.copyTree
semOpAux(c, n)
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 c.config.m.errorOutputs == {}:
# speed up error generation:
globalError(c.config, n.info, "type mismatch")
return c.graph.emptyNode
else:
var hasErrorType = false
var msg = "type mismatch: got <"
for i in 1..<n.len:
if i > 1: msg.add(", ")
let nt = n[i].typ
msg.add(typeToString(nt))
if nt.kind == tyError:
hasErrorType = true
break
if not hasErrorType:
let typ = n[0].typ
msg.add(">\nbut expected one of:\n" &
typeToString(typ))
# prefer notin preferToResolveSymbols
# t.sym != nil
# sfAnon notin t.sym.flags
# t.kind != tySequence(It is tyProc)
if typ.sym != nil and sfAnon notin typ.sym.flags and
typ.kind == tyProc:
# when can `typ.sym != nil` ever happen?
msg.add(" = " & typeToString(typ, preferDesc))
msg.addDeclaredLocMaybe(c.config, typ)
localError(c.config, n.info, msg)
return errorNode(c, n)
else:
result = m.call
instGenericConvertersSons(c, result, m)
markConvertersUsed(c, result)
else:
result = overloadedCallOpr(c, n) # this uses efNoUndeclared
# 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 or result.kind == nkEmpty:
# XXX: hmm, what kind of symbols will end up here?
# do we really need to try the overload resolution?
n[0] = prc
nOrig[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[0].kind == nkSym:
result = afterCallActions(c, result, nOrig, flags, expectedType)
else:
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
# this seems to be a hotspot in the compiler!
let nOrig = n.copyTree
#semLazyOpAux(c, n)
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags, expectedType)
if result != nil: result = afterCallActions(c, result, nOrig, flags, expectedType)
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)
let e = systemModuleSym(c.graph, getIdent(c.cache, "echo"))
if e != nil:
result.add(newSymNode(e))
else:
result.add localErrorNode(c, n, "system needs: echo")
result.add(n)
result.add(newStrNode(nkStrLit, ": " & n.typ.typeToString))
result = semExpr(c, result)
proc semExprNoType(c: PContext, n: PNode): PNode =
let isPush = c.config.hasHint(hintExtendedContext)
if isPush: pushInfoContext(c.config, n.info)
result = semExpr(c, n, {efWantStmt})
discardCheck(c, result, {})
if isPush: popInfoContext(c.config)
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 0..<r.len:
result = lookupInRecordAndBuildCheck(c, n, r[i], field, check)
if result != nil: return
of nkRecCase:
checkMinSonsLen(r, 2, c.config)
if (r[0].kind != nkSym): illFormedAst(r, c.config)
result = lookupInRecordAndBuildCheck(c, n, r[0], field, check)
if result != nil: return
let setType = createSetType(c, r[0].typ)
var s = newNodeIT(nkCurly, r.info, setType)
for i in 1..<r.len:
var it = r[i]
case it.kind
of nkOfBranch:
result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check)
if result == nil:
for j in 0..<it.len-1: s.add copyTree(it[j])
else:
if check == nil:
check = newNodeI(nkCheckedFieldExpr, n.info)
check.add c.graph.emptyNode # make space for access node
s = newNodeIT(nkCurly, n.info, setType)
for j in 0..<it.len - 1: s.add copyTree(it[j])
var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
inExpr.add newSymNode(getSysMagic(c.graph, n.info, "contains", mInSet), n.info)
inExpr.add s
inExpr.add copyTree(r[0])
check.add inExpr
#check.add 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)
check.add c.graph.emptyNode # make space for access node
var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
inExpr.add newSymNode(getSysMagic(c.graph, n.info, "contains", mInSet), n.info)
inExpr.add s
inExpr.add copyTree(r[0])
var notExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
notExpr.add newSymNode(getSysMagic(c.graph, n.info, "not", mNot), n.info)
notExpr.add inExpr
check.add notExpr
return
else: illFormedAst(it, c.config)
of nkSym:
if r.sym.name.id == field.id: result = r.sym
else: illFormedAst(n, c.config)
const
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
tyDotOpTransparent = {tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink}
proc readTypeParameter(c: PContext, typ: PType,
paramName: PIdent, info: TLineInfo): PNode =
# Note: This function will return emptyNode when attempting to read
# a static type parameter that is not yet resolved (e.g. this may
# happen in proc signatures such as `proc(x: T): array[T.sizeParam, U]`
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, c.idgen).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.firstGenericParam.skipGenericAlias
else: typ.skipGenericAlias
let tbody = ty[0]
for s in 0..<tbody.len-1:
let tParam = tbody[s]
if tParam.sym.name.id == paramName.id:
let rawTyp = ty[s + 1]
if rawTyp.kind == tyStatic:
if rawTyp.n != nil:
return rawTyp.n
else:
return c.graph.emptyNode
else:
let foundTyp = makeTypeDesc(c, rawTyp)
return newSymNode(copySym(tParam.sym, c.idgen).linkTo(foundTyp), info)
return nil
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
result = nil
assert n.kind in nkIdentKinds + {nkDotExpr}
let s = getGenSym(c, sym)
case s.kind
of skConst:
if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
markUsed(c, n.info, s)
onUse(n.info, s)
let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc})
case typ.kind
of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyTuple, tySet, tyUInt..tyUInt64:
if s.magic == mNone: result = inlineConst(c, 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.astdef.safeLen == 0: result = inlineConst(c, n, s)
else: result = newSymNode(s, n.info)
of tyStatic:
if typ.n != nil:
result = typ.n
result.typ() = typ.base
else:
result = newSymNode(s, n.info)
else:
result = newSymNode(s, n.info)
of skMacro, skTemplate:
# check if we cannot use alias syntax (no required args or generic params)
if sfNoalias in s.flags:
let info = getCallLineInfo(n)
markUsed(c, info, s)
onUse(info, s)
result = symChoice(c, n, s, scClosed)
else:
case s.kind
of skMacro: result = semMacroExpr(c, n, n, s, flags)
of skTemplate: result = semTemplateExpr(c, n, s, flags)
else: discard # unreachable
of skParam:
markUsed(c, n.info, s)
onUse(n.info, s)
if s.typ != nil and 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:
# the owner should have been set by now by addParamOrResult
internalAssert c.config, s.owner != nil
result = newSymNode(s, n.info)
of skVar, skLet, skResult, skForVar:
if s.magic == mNimvm:
localError(c.config, n.info, "illegal context for 'nimvm' magic")
if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
markUsed(c, n.info, s)
onUse(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?
if hasWarn(c.config, warnResultUsed):
message(c.config, n.info, warnResultUsed)
of skGenericParam:
onUse(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:
if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
markUsed(c, n.info, s)
onUse(n.info, s)
if s.typ == nil:
return localErrorNode(c, n, "symbol '$1' has no type" % [s.name.s])
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
return s.typ.n
result = newSymNode(s, n.info)
result.typ() = makeTypeDesc(c, s.typ)
of skField:
# old code, not sure if it's live code:
markUsed(c, n.info, s)
onUse(n.info, s)
result = newSymNode(s, n.info)
of skModule:
# make sure type is None and not nil for discard checking
if efWantStmt in flags: s.typ = newTypeS(tyNone, c)
markUsed(c, n.info, s)
onUse(n.info, s)
result = newSymNode(s, n.info)
else:
let info = getCallLineInfo(n)
#if efInCall notin flags:
markUsed(c, info, s)
onUse(info, s)
result = newSymNode(s, info)
proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode =
case t.kind
of tyGenericInst:
result = readTypeParameter(c, t, i, n.info)
if result == c.graph.emptyNode:
if c.inGenericContext > 0:
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
else:
result = nil
of tyUserTypeClasses:
if t.isResolvedUserTypeClass:
result = readTypeParameter(c, t, i, n.info)
elif c.inGenericContext > 0:
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, copyTree(result))
else:
result = nil
of tyGenericBody, tyCompositeTypeClass:
if c.inGenericContext > 0:
result = readTypeParameter(c, t, i, n.info)
if result != nil:
# generic parameter exists, stop here but delay until instantiation
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, copyTree(result))
else:
result = nil
elif c.inGenericContext > 0 and t.containsUnresolvedType:
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, copyTree(result))
else:
result = nil
proc tryReadingTypeField(c: PContext, n: PNode, i: PIdent, ty: PType): PNode =
result = nil
var ty = ty.skipTypes(tyDotOpTransparent)
case ty.kind
of tyEnum:
# look up if the identifier belongs to the enum:
var f = PSym(nil)
while ty != nil:
f = getSymFromList(ty.n, i)
if f != nil: break
ty = ty[0] # enum inheritance
if f != nil:
result = newSymNode(f)
result.info = n.info
result.typ() = ty
markUsed(c, n.info, f)
onUse(n.info, f)
of tyObject, tyTuple:
if ty.n != nil and ty.n.kind == nkRecList:
let field = lookupInRecord(ty.n, i)
if field != nil:
n.typ() = makeTypeDesc(c, field.typ)
result = n
of tyGenericInst:
result = tryReadingTypeField(c, n, i, ty.skipModifier)
if result == nil:
result = tryReadingGenericParam(c, n, i, ty)
else:
result = tryReadingGenericParam(c, n, i, ty)
proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
## returns nil if it's not a built-in field access
checkSonsLen(n, 2, c.config)
# 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[1]): return
when defined(nimsuggest):
if c.config.cmd == cmdIdeTools:
suggestExpr(c, n)
if exactEquals(c.config.m.trackPos, 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(c, n[1].info, s)
result = semSym(c, n, s, flags)
onUse(n[1].info, s)
return
# extra flags since LHS may become a call operand:
n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable, efAllowSymChoice})
#restoreOldStyleType(n[0])
var i = considerQuotedIdent(c, n[1], n)
var ty = n[0].typ
var f: PSym = nil
result = nil
if ty.kind == tyTypeDesc:
if ty.base.kind == tyNone:
# This is a still unresolved typedesc parameter.
# If this is a regular proc, then all bets are off and we must return
# tyFromExpr, but when this happen in a macro this is not a built-in
# field access and we leave the compiler to compile a normal call:
if getCurrOwner(c).kind != skMacro:
n.typ() = makeTypeFromExpr(c, n.copyTree)
flags.incl efCannotBeDotCall
return n
else:
return nil
else:
flags.incl efCannotBeDotCall
return tryReadingTypeField(c, n, i, ty.base)
elif isTypeExpr(n.sons[0]):
flags.incl efCannotBeDotCall
return tryReadingTypeField(c, n, i, ty)
elif ty.kind == tyError:
# a type error doesn't have any builtin fields
return nil
if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass:
ty = ty.last
ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink, tyStatic})
while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst, tyAlias})
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[0] == nil: break
ty = skipTypes(ty[0], skipPtrs)
if f != nil:
let visibilityCheckNeeded =
if n[1].kind == nkSym and n[1].sym == f:
false # field lookup was done already, likely by hygienic template or bindSym
else: true
if not visibilityCheckNeeded or fieldVisible(c, f):
# is the access to a public field or in the same module or in a friend?
markUsed(c, n[1].info, f)
onUse(n[1].info, f)
let info = n[1].info
n[0] = makeDeref(n[0])
n[1] = newSymNode(f) # we now have the correct field
n[1].info = info # preserve the original info
n.typ() = f.typ
if check == nil:
result = n
else:
check[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(c, n[1].info, f)
onUse(n[1].info, f)
n[0] = makeDeref(n[0])
n[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[0].typ.skipTypes(tyDotOpTransparent)
result = tryReadingGenericParam(c, n, i, t)
flags.incl efCannotBeDotCall
proc dotTransformation(c: PContext, n: PNode): PNode =
if isSymChoice(n[1]) or
# generics usually leave field names as symchoices, but not types
(n[1].kind == nkSym and n[1].sym.kind == skType):
result = newNodeI(nkDotCall, n.info)
result.add n[1]
result.add copyTree(n[0])
else:
var i = considerQuotedIdent(c, n[1], n)
result = newNodeI(nkDotCall, n.info)
result.flags.incl nfDotField
result.add newIdentNode(i, n[1].info)
result.add 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.
var f = flags - {efIsDotCall}
result = builtinFieldAccess(c, n, f)
if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and
efIsDotCall in flags and callOperator notin c.features and
efCannotBeDotCall notin f):
result = dotTransformation(c, n)
proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
result = newNodeI(nkCall, n.info)
result.add(newIdentNode(ident, n.info))
for s in n: result.add s
proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode =
checkSonsLen(n, 1, c.config)
n[0] = semExprWithType(c, n[0])
let a = getConstExpr(c.module, n[0], c.idgen, c.graph)
if a != nil:
if a.kind == nkNilLit and efInTypeof notin flags:
localError(c.config, n.info, "nil dereference is not allowed")
n[0] = a
result = n
var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned})
case t.kind
of tyRef, tyPtr: n.typ() = t.elementType
of tyMetaTypes, tyFromExpr:
n.typ() = makeTypeFromExpr(c, n.copyTree)
else: result = nil
#GlobalError(n[0].info, errCircumNeedsPointer)
proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym, doError: bool): PNode =
## Attempts to instantiate generic proc symbol(s) with given parameters.
## If instantiation causes errors; if `doError` is `true`, a type mismatch
## error is given, otherwise `nil` is returned.
result = explicitGenericInstantiation(c, n, s, doError)
if result == n:
n[0] = copyTree(result[0])
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags, afterOverloading = false): PNode =
## returns nil if not a built-in subscript operator; also called for the
## checking of assignments
result = nil
if n.len == 1:
let x = semDeref(c, n, flags)
if x == nil: return nil
if x.typ.kind == tyFromExpr:
# depends on generic type
return x
result = newNodeIT(nkDerefExpr, x.info, x.typ)
result.add(x[0])
return
checkMinSonsLen(n, 2, c.config)
# signal that generic parameters may be applied after
n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric, efAllowSymChoice})
var arr = skipTypes(n[0].typ, {tyGenericInst, tyUserTypeClassInst, tyOwned,
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
if arr.kind == tyStatic:
if arr.base.kind == tyNone:
result = n
result.typ() = semStaticType(c, n[1], nil)
return
elif arr.n != nil:
return semSubscript(c, arr.n, flags, afterOverloading)
else:
arr = arr.base
case arr.kind
of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCstring,
tyUncheckedArray:
if n.len != 2: return nil
n[0] = makeDeref(n[0])
for i in 1..<n.len:
n[i] = semExprWithType(c, n[i],
flags*{efInTypeof, efDetermineType})
# Arrays index type is dictated by the range's type
if arr.kind == tyArray:
var indexType = arr[0]
var arg = indexTypesMatch(c, indexType, n[1].typ, n[1])
if arg != nil:
n[1] = arg
result = n
result.typ() = elemType(arr)
# Other types have a bit more of leeway
elif n[1].typ.skipTypes(abstractRange-{tyDistinct}).kind in
{tyInt..tyInt64, tyUInt..tyUInt64}:
result = n
result.typ() = elemType(arr)
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:
if n.len != 2: return nil
n[0] = makeDeref(n[0])
# [] operator for tuples requires constant expression:
n[1] = semConstExpr(c, n[1])
if skipTypes(n[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in
{tyInt..tyInt64}:
let idx = getOrdValue(n[1])
if idx >= 0 and idx < arr.len: n.typ() = arr[toInt(idx)]
else:
localError(c.config, n.info,
"invalid index $1 in subscript for tuple of length $2" %
[$idx, $arr.len])
result = n
else:
result = nil
else:
let s = if n[0].kind == nkSym: n[0].sym
elif n[0].kind in nkSymChoices + {nkOpenSym}: n[0][0].sym
else: nil
if s != nil:
case s.kind
of skProc, skFunc, skMethod, skConverter, skIterator:
# type parameters: partial generic specialization
n[0] = semSymGenericInstantiation(c, n[0], s)
result = maybeInstantiateGeneric(c, n, s, doError = afterOverloading)
if result != nil and
# leave untyped generic expression alone:
(result.typ == nil or result.typ.kind != tyFromExpr):
# check newly created sym/symchoice
result = semExpr(c, result, flags)
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.transitionSonsKind(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:
discard
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = semSubscript(c, n, flags)
if result == nil:
# overloaded [] operator:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")), flags, expectedType)
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerQuotedIdent(c, a[1], a)
var setterId = newIdentNode(getIdent(c.cache, 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 = newTreeI(nkCall, n.info, setterId, a[0], n[1])
result.flags.incl nfDotSetter
let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1])
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
if result != nil:
result = afterCallActions(c, result, nOrig, {})
#fixAbstractType(c, result)
#analyseIfAddressTakenInCall(c, result)
proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
# See RFC #7373, calls returning 'var T' are assumed to
# return a view into the first argument (if there is one):
let root = exprRoot(n)
if root != nil and root.owner == c.p.owner:
template url: string = "var_t_return.html".createDocLink
if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags:
localError(c.config, n.info, "'$1' escapes its stack frame; context: '$2'; see $3" % [
root.name.s, renderTree(n, {renderNoComments}), url])
elif root.kind == skParam and root.position != 0:
localError(c.config, n.info, "'$1' is not the first parameter; context: '$2'; see $3" % [
root.name.s, renderTree(n, {renderNoComments}), url])
case n.kind
of nkHiddenAddr, nkAddr: return n
of nkDerefExpr: return n[0]
of nkBracketExpr:
if n.len == 1: return n[0]
of nkHiddenDeref:
# issue #13848
# `proc fun(a: var int): var int = a`
discard
else: discard
let valid = isAssignable(c, n)
if valid != arLValue:
if valid in {arAddressableConst, arLentValue} and isLent:
discard "ok"
elif valid == arLocalLValue:
localError(c.config, n.info, errXStackEscape % renderTree(n, {renderNoComments}))
else:
localError(c.config, n.info, errExprHasNoAddress)
result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ))
if n.typ.kind in {tyVar, tyLent}:
n.typ() = n.typ.elementType
result.add(n)
proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
if le.kind == nkHiddenDeref:
var x = le[0]
if x.kind == nkSym:
if x.sym.kind == skResult and (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView):
n[0] = x # 'result[]' --> 'result'
n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent)
x.typ.flags.incl tfVarIsPtr
#echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
elif sfGlobal in x.sym.flags:
x.typ.flags.incl tfVarIsPtr
proc borrowCheck(c: PContext, n, le, ri: PNode) =
const
PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
nkBracketExpr, nkAddr, nkHiddenAddr,
nkObjDownConv, nkObjUpConv}
PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
proc getRoot(n: PNode; followDeref: bool): PNode =
result = n
while true:
case result.kind
of nkDerefExpr, nkHiddenDeref:
if followDeref: result = result[0]
else: break
of PathKinds0:
result = result[0]
of PathKinds1:
result = result[1]
else: break
proc scopedLifetime(c: PContext; ri: PNode): bool {.inline.} =
let n = getRoot(ri, followDeref = false)
result = (ri.kind in nkCallKinds+{nkObjConstr}) or
(n.kind == nkSym and n.sym.owner == c.p.owner and n.sym.kind != skResult)
proc escapes(c: PContext; le: PNode): bool {.inline.} =
# param[].foo[] = self definitely escapes, we don't need to
# care about pointer derefs:
let n = getRoot(le, followDeref = true)
result = n.kind == nkSym and n.sym.kind == skParam
# Special typing rule: do not allow to pass 'owned T' to 'T' in 'result = x':
const absInst = abstractInst - {tyOwned}
if ri.typ != nil and ri.typ.skipTypes(absInst).kind == tyOwned and
le.typ != nil and le.typ.skipTypes(absInst).kind != tyOwned and
scopedLifetime(c, ri):
if le.kind == nkSym and le.sym.kind == skResult:
localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " &
"use 'owned(" & typeToString(le.typ) & ")' as the return type")
elif escapes(c, le):
localError(c.config, n.info,
"assignment produces a dangling ref: the unowned ref lives longer than the owned ref")
template resultTypeIsInferrable(typ: PType): untyped =
typ.isMetaType and typ.kind != tyTypeDesc
proc goodLineInfo(arg: PNode): TLineInfo =
if arg.kind == nkStmtListExpr and arg.len > 0:
goodLineInfo(arg[^1])
else:
arg.info
proc makeTupleAssignments(c: PContext; n: PNode): PNode =
## expand tuple unpacking assignment into series of assignments
##
## mirrored with semstmts.makeVarTupleSection
let lhs = n[0]
let value = semExprWithType(c, n[1], {efTypeAllowed})
if value.typ.kind != tyTuple:
localError(c.config, n[1].info, errTupleUnpackingTupleExpected %
[typeToString(value.typ, preferDesc)])
elif lhs.len != value.typ.len:
localError(c.config, n.info, errTupleUnpackingDifferentLengths %
[$lhs.len, typeToString(value.typ, preferDesc), $value.typ.len])
result = newNodeI(nkStmtList, n.info)
let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
temp.typ = value.typ
temp.flags.incl(sfGenSym)
var v = newNodeI(nkLetSection, value.info)
let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
var vpart = newNodeI(nkIdentDefs, v.info, 3)
vpart[0] = tempNode
vpart[1] = c.graph.emptyNode
vpart[2] = value
v.add vpart
result.add(v)
for i in 0..<lhs.len:
if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
# skip _ assignments if we are using a temp as they are already evaluated
discard
else:
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
checkSonsLen(n, 2, c.config)
var a = n[0]
case a.kind
of nkDotExpr:
# r.f = x
# --> `f=` (r, x)
let nOrig = n.copyTree
var flags = {efLValue}
a = builtinFieldAccess(c, a, flags)
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.transitionSonsKind(nkCall)
a = semExprWithType(c, a, {efLValue})
of nkBracketExpr:
# a[i] = x
# --> `[]=`(a, i, x)
a = semSubscript(c, a, {efLValue})
if a == nil:
result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]="))
result.add(n[1])
if mode == noOverloadedSubscript:
bracketNotFoundError(c, result, {})
return errorNode(c, n)
else:
result = semExprNoType(c, result)
return result
of nkCurlyExpr:
# a{i} = x --> `{}=`(a, i, x)
result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "{}="))
result.add(n[1])
return semExprNoType(c, result)
of nkPar, nkTupleConstr:
if a.len >= 2 or a.kind == nkTupleConstr:
# 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, makeTupleAssignments(c, n), {})
else:
a = semExprWithType(c, a, {efLValue})
else:
a = semExprWithType(c, a, {efLValue})
n[0] = a
# a = b # both are vars, means: a[] = b[]
# a = b # b no 'var T' means: a = addr(b)
var le = a.typ
let assignable = isAssignable(c, a)
let root = getRoot(a)
let useStrictDefLet = root != nil and root.kind == skLet and
assignable == arAddressableConst and
strictDefs in c.features and isLocalSym(root)
if le == nil:
localError(c.config, a.info, "expression has no type")
elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind notin {tyVar} and
assignable in {arNone, arLentValue, arAddressableConst} and not useStrictDefLet
) or (skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs} and views notin c.features):
# Direct assignment to a discriminant is allowed!
localError(c.config, a.info, errXCannotBeAssignedTo %
renderTree(a, {renderNoComments}))
else:
let lhs = n[0]
let rhs = semExprWithType(c, n[1], {efTypeAllowed}, le)
if lhs.kind == nkSym and lhs.sym.kind == skResult:
n.typ() = c.enforceVoidContext
if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
var rhsTyp = rhs.typ
if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass:
rhsTyp = rhsTyp.last
if lhs.sym.typ.kind == tyAnything:
rhsTyp = rhsTyp.skipTypes({tySink}).skipIntLit(c.idgen)
if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}:
internalAssert c.config, c.p.resultSym != nil
# Make sure the type is valid for the result variable
typeAllowedCheck(c, n.info, rhsTyp, skResult)
lhs.typ() = rhsTyp
c.p.resultSym.typ = rhsTyp
c.p.owner.typ.setReturnType rhsTyp
else:
typeMismatch(c.config, n.info, lhs.typ, rhsTyp, rhs)
borrowCheck(c, n, lhs, rhs)
n[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
when false: liftTypeBoundOps(c, lhs.typ, lhs.info)
fixAbstractType(c, n)
asgnToResultVar(c, n, n[0], n[1])
result = n
proc semReturn(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1, c.config)
if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or
(not c.p.owner.typ.isNil and isClosureIterator(c.p.owner.typ)):
if n[0].kind != nkEmpty:
if n[0].kind == nkAsgn and n[0][0].kind == nkSym and c.p.resultSym == n[0][0].sym:
discard "return is already transformed"
elif c.p.resultSym != nil:
# transform ``return expr`` to ``result = expr; return``
var a = newNodeI(nkAsgn, n[0].info)
a.add newSymNode(c.p.resultSym)
a.add n[0]
n[0] = a
else:
localError(c.config, n.info, errNoReturnTypeDeclared)
return
result[0] = semAsgn(c, n[0])
# optimize away ``result = result``:
if result[0][1].kind == nkSym and result[0][1].sym == c.p.resultSym:
result[0] = c.graph.emptyNode
else:
localError(c.config, n.info, "'return' not allowed here")
proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode =
when defined(nimsuggest):
if c.graph.config.expandDone():
return n
openScope(c)
result = semExpr(c, n, expectedType = expectedType)
if c.p.resultSym != nil and not isEmptyType(result.typ):
if 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(c, result)
else:
var a = newNodeI(nkAsgn, n.info, 2)
a[0] = newSymNode(c.p.resultSym)
a[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.setReturnType nil
else:
localError(c.config, c.p.resultSym.info, errCannotInferReturnType %
c.p.owner.name.s)
if isIterator(c.p.owner.typ) and c.p.owner.typ.returnType != nil and
c.p.owner.typ.returnType.kind == tyAnything:
localError(c.config, c.p.owner.info, errCannotInferReturnType %
c.p.owner.name.s)
closeScope(c)
proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
var t = skipTypes(restype, {tyGenericInst, tyAlias, tySink})
case t.kind
of tyVar, tyLent:
t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
if n[0].kind in {nkHiddenStdConv, nkHiddenSubConv}:
n[0] = n[0][1]
n[0] = takeImplicitAddr(c, n[0], t.kind == tyLent)
of tyTuple:
for i in 0..<t.len:
let e = skipTypes(t[i], {tyGenericInst, tyAlias, tySink})
if e.kind in {tyVar, tyLent}:
e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
let tupleConstr = if n[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: n[0][1] else: n[0]
if tupleConstr.kind in {nkPar, nkTupleConstr}:
if tupleConstr[i].kind == nkExprColonExpr:
tupleConstr[i][1] = takeImplicitAddr(c, tupleConstr[i][1], e.kind == tyLent)
else:
tupleConstr[i] = takeImplicitAddr(c, tupleConstr[i], e.kind == tyLent)
else:
localError(c.config, n[0].info, errXExpected, "tuple constructor")
elif e.kind == tyEmpty:
localError(c.config, n[0].info, errTypeExpected)
else:
when false:
# XXX investigate what we really need here.
if isViewType(t):
n[0] = takeImplicitAddr(c, n[0], false)
proc semYield(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1, c.config)
if c.p.owner == nil or c.p.owner.kind != skIterator:
localError(c.config, n.info, errYieldNotAllowedHere)
elif n[0].kind != nkEmpty:
var iterType = c.p.owner.typ
let restype = iterType[0]
n[0] = semExprWithType(c, n[0], {}, restype) # check for type compatibility:
if restype != nil:
if n[0].typ == nil: internalError(c.config, n.info, "semYield")
if resultTypeIsInferrable(restype):
let inferred = n[0].typ
iterType[0] = inferred
if c.p.resultSym != nil:
c.p.resultSym.typ = inferred
else:
n[0] = fitNode(c, restype, n[0], n.info)
semYieldVarResult(c, n, restype)
else:
localError(c.config, n.info, errCannotReturnExpr)
elif c.p.owner.typ.returnType != nil:
localError(c.config, n.info, errGenerated, "yield statement must yield a value")
proc considerQuotedIdentOrDot(c: PContext, n: PNode, origin: PNode = nil): PIdent =
if n.kind == nkDotExpr:
let a = considerQuotedIdentOrDot(c, n[0], origin).s
let b = considerQuotedIdentOrDot(c, n[1], origin).s
var s = newStringOfCap(a.len + b.len + 1)
s.add(a)
s.add('.')
s.add(b)
result = getIdent(c.cache, s)
else:
result = considerQuotedIdent(c, n, origin)
proc semDefined(c: PContext, n: PNode): PNode =
checkSonsLen(n, 2, c.config)
# we replace this node by a 'true' or 'false' node:
result = newIntNode(nkIntLit, 0)
result.intVal = ord isDefined(c.config, considerQuotedIdentOrDot(c, n[1], n).s)
result.info = n.info
result.typ() = getSysType(c.graph, n.info, tyBool)
proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
case n.kind
of nkIdent, nkAccQuoted:
var amb = false
let ident = considerQuotedIdent(c, n)
result = if onlyCurrentScope:
localSearchInScope(c, ident)
else:
searchInScopes(c, ident, amb)
of nkDotExpr:
result = nil
if onlyCurrentScope: return
checkSonsLen(n, 2, c.config)
var m = lookUpForDeclared(c, n[0], onlyCurrentScope)
if m != nil and m.kind == skModule:
let ident = considerQuotedIdent(c, n[1], n)
if m == c.module:
result = strTableGet(c.topLevelScope.symbols, ident)
else:
result = someSym(c.graph, m, ident)
of nkSym:
result = n.sym
of nkOpenSymChoice, nkClosedSymChoice:
result = n[0].sym
of nkOpenSym:
result = lookUpForDeclared(c, n[0], onlyCurrentScope)
else:
localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
result = nil
proc semDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
checkSonsLen(n, 2, c.config)
# we replace this node by a 'true' or 'false' node:
result = newIntNode(nkIntLit, 0)
result.intVal = ord lookUpForDeclared(c, n[1], onlyCurrentScope) != nil
result.info = n.info
result.typ() = getSysType(c.graph, n.info, 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(c.config, n.info, "'$1' is not a macro or template" % expandedSym.name.s)
return errorSym(c, n[0])
result = expandedSym
else:
localError(c.config, n.info, "'$1' is not a macro or template" % 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:
result = ""
localError(c.config, n.info, errStringLiteralExpected)
proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
result = newSym(kind, c.cache.idAnon, c.idgen, getCurrOwner(c), info)
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[0] = newSymNode(expandedSym, macroCall.info)
markUsed(c, n.info, expandedSym)
onUse(n.info, expandedSym)
if isCallExpr(macroCall):
for i in 1..<macroCall.len:
#if macroCall[0].typ[i].kind != tyUntyped:
macroCall[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 = default(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(c.config, n.info, "expected a template that takes " & $(macroCall.len-1) & " arguments")
elif cands >= 2:
localError(c.config, n.info, "ambiguous symbol in 'getAst' context: " & $macroCall)
else:
let info = macroCall[0].info
macroCall[0] = newSymNode(cand, info)
markUsed(c, info, cand)
onUse(info, cand)
# we just perform overloading resolution here:
#n[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro})
else:
localError(c.config, n.info, "getAst takes a call, but got " & n.renderTree)
# Preserve the magic symbol in order to be handled in evals.nim
internalAssert c.config, n[0].sym.magic == mExpandToAst
#n.typ() = getSysSym("NimNode").typ # expandedSym.getReturnType
if n.kind == nkStmtList and n.len == 1: result = n[0]
else: result = n
result.typ() = sysTypeFromName(c.graph, n.info, "NimNode")
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
flags: TExprFlags = {}): PNode =
if n.len == 2:
n[0] = newSymNode(magicSym, n.info)
result = semExpandToAst(c, n)
else:
result = semDirectOp(c, n, flags)
proc processQuotations(c: PContext; n: var PNode, op: string,
quotes: var seq[PNode],
ids: var seq[PNode]) =
template returnQuote(q) =
quotes.add q
n = newIdentNode(getIdent(c.cache, $quotes.len), n.info)
ids.add n
return
template handlePrefixOp(prefixed) =
if prefixed[0].kind == nkIdent:
let examinedOp = prefixed[0].ident.s
if examinedOp == op:
returnQuote prefixed[1]
elif examinedOp.startsWith(op):
prefixed[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), prefixed.info)
if n.kind == nkPrefix:
checkSonsLen(n, 2, c.config)
handlePrefixOp(n)
elif n.kind == nkAccQuoted:
if op == "``":
returnQuote n[0]
else: # [bug #7589](https://github.com/nim-lang/Nim/issues/7589)
if n.len == 2 and n[0].ident.s == op:
var tempNode = nkPrefix.newTree()
tempNode.newSons(2)
tempNode[0] = n[0]
tempNode[1] = n[1]
handlePrefixOp(tempNode)
elif n.kind == nkIdent:
if n.ident.s == "result":
n = ids[0]
for i in 0..<n.safeLen:
processQuotations(c, n[i], op, quotes, ids)
proc semQuoteAst(c: PContext, n: PNode): PNode =
if n.len != 2 and n.len != 3:
localError(c.config, n.info, "'quote' expects 1 or 2 arguments")
return n
# We transform the do block into a template with a param for
# each interpolation. We'll pass this template to getAst.
var
quotedBlock = n[^1]
op = if n.len == 3: expectString(c, n[1]) else: "``"
quotes = newSeq[PNode](2)
# the quotes will be added to a nkCall statement
# leave some room for the callee symbol and the result symbol
ids = newSeq[PNode](1)
# this will store the generated param names
# leave some room for the result symbol
if quotedBlock.kind != nkStmtList:
localError(c.config, n.info, errXExpected, "block")
# This adds a default first field to pass the result symbol
ids[0] = newAnonSym(c, skParam, n.info).newSymNode
processQuotations(c, quotedBlock, op, quotes, ids)
let dummyTemplateSym = newAnonSym(c, skTemplate, n.info)
incl(dummyTemplateSym.flags, sfTemplateRedefinition)
var dummyTemplate = newProcNode(
nkTemplateDef, quotedBlock.info, body = quotedBlock,
params = c.graph.emptyNode,
name = dummyTemplateSym.newSymNode,
pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode)
if ids.len > 0:
dummyTemplate[paramsPos] = newNodeI(nkFormalParams, n.info)
dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
for i in 1..<ids.len:
let exp = semExprWithType(c, quotes[i+1], {})
let typ = exp.typ
if tfTriggersCompileTime notin typ.flags and typ.kind != tyStatic and exp.kind == nkSym and exp.sym.kind notin routineKinds + {skType}:
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], newNodeIT(nkType, n.info, typ), c.graph.emptyNode)
else:
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
var tmpl = semTemplateDef(c, dummyTemplate)
quotes[0] = tmpl[namePos]
# This adds a call to newIdentNode("result") as the first argument to the template call
let identNodeSym = getCompilerProc(c.graph, "newIdentNode")
# so that new Nim compilers can compile old macros.nim versions, we check for 'nil'
# here and provide the old fallback solution:
let identNode = if identNodeSym == nil:
newIdentNode(getIdent(c.cache, "newIdentNode"), n.info)
else:
identNodeSym.newSymNode
quotes[1] = newTreeI(nkCall, n.info, identNode, newStrNode(nkStrLit, "result"))
result = newTreeI(nkCall, n.info,
createMagic(c.graph, c.idgen, "getAst", mExpandToAst).newSymNode,
newTreeI(nkCall, n.info, quotes))
result = semExpandToAst(c, result)
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# watch out, hacks ahead:
when defined(nimsuggest):
# Remove the error hook so nimsuggest doesn't report errors there
let tempHook = c.graph.config.structuredErrorHook
c.graph.config.structuredErrorHook = nil
let oldErrorCount = c.config.errorCounter
let oldErrorMax = c.config.errorMax
let oldCompilesId = c.compilesContextId
# if this is a nested 'when compiles', do not increase the ID so that
# generic instantiations can still be cached for this level.
if c.compilesContextId == 0:
inc c.compilesContextIdGenerator
c.compilesContextId = c.compilesContextIdGenerator
c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here
# open a scope for temporary symbol inclusions:
let oldScope = c.currentScope
openScope(c)
let oldOwnerLen = c.graph.owners.len
let oldGenerics = c.generics
let oldErrorOutputs = c.config.m.errorOutputs
if efExplain notin flags: c.config.m.errorOutputs = {}
let oldContextLen = msgs.getInfoContextLen(c.config)
let oldInGenericContext = c.inGenericContext
let oldInUnrolledContext = c.inUnrolledContext
let oldInGenericInst = c.inGenericInst
let oldInStaticContext = c.inStaticContext
let oldProcCon = c.p
c.generics = @[]
var err: string
try:
result = semExpr(c, n, flags)
if result != nil and efNoSem2Check notin flags:
trackStmt(c, c.module, result, isTopLevel = false)
if c.config.errorCounter != oldErrorCount:
result = nil
except ERecoverableError:
result = nil
# 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.inStaticContext = oldInStaticContext
c.p = oldProcCon
msgs.setInfoContextLen(c.config, oldContextLen)
setLen(c.graph.owners, oldOwnerLen)
c.currentScope = oldScope
c.config.m.errorOutputs = oldErrorOutputs
c.config.errorCounter = oldErrorCount
c.config.errorMax = oldErrorMax
when defined(nimsuggest):
# Restore the error hook
c.graph.config.structuredErrorHook = tempHook
proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we replace this node by a 'true' or 'false' node:
if n.len != 2: return semDirectOp(c, n, flags)
result = newIntNode(nkIntLit, ord(tryExpr(c, n[1], flags) != nil))
result.info = n.info
result.typ() = getSysType(c.graph, n.info, tyBool)
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
if n.len == 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.idgen, c.module)
addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ, c.idgen)
addSonSkipIntLit(result, t, c.idgen)
result = instGenericContainer(c, info, result, allowMetaTypes = false)
proc instantiateCreateFlowVarCall(c: PContext; t: PType;
info: TLineInfo): PSym =
let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar")
if sym == nil:
localError(c.config, info, "system needs: nimCreateFlowVar")
var bindings = initLayeredTypeMap()
bindings.put(sym.ast[genericParamsPos][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.excl {sfCompilerProc, sfExportc, sfImportc}
result.loc.snippet = ""
proc setMs(n: PNode, s: PSym): PNode =
result = n
n[0] = newSymNode(s)
n[0].info = n.info
proc semSizeof(c: PContext, n: PNode): PNode =
if n.len != 2:
localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof")
else:
n[1] = semExprWithType(c, n[1], {efDetermineType})
#restoreOldStyleType(n[1])
n.typ() = getSysType(c.graph, n.info, tyInt)
result = foldSizeOf(c.config, n, n)
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode =
# this is a hotspot in the compiler!
result = n
case s.magic # magics that need special treatment
of mAddr:
markUsed(c, n.info, s)
checkSonsLen(n, 2, c.config)
result = semAddr(c, n[1])
of mTypeOf:
markUsed(c, n.info, s)
result = semTypeOf(c, n)
of mDefined:
markUsed(c, n.info, s)
result = semDefined(c, setMs(n, s))
of mDeclared:
markUsed(c, n.info, s)
result = semDeclared(c, setMs(n, s), false)
of mDeclaredInScope:
markUsed(c, n.info, s)
result = semDeclared(c, setMs(n, s), true)
of mCompiles:
markUsed(c, n.info, s)
result = semCompiles(c, setMs(n, s), flags)
of mIs:
markUsed(c, n.info, s)
result = semIs(c, setMs(n, s), flags)
of mShallowCopy:
markUsed(c, n.info, s)
result = semShallowCopy(c, n, flags)
of mExpandToAst:
markUsed(c, n.info, s)
result = semExpandToAst(c, n, s, flags)
of mQuoteAst:
markUsed(c, n.info, s)
result = semQuoteAst(c, n)
of mAstToStr:
markUsed(c, n.info, s)
checkSonsLen(n, 2, c.config)
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph)
result.typ() = getSysType(c.graph, n.info, tyString)
of mParallel:
markUsed(c, n.info, s)
if parallel notin c.features:
localError(c.config, n.info, "use the {.experimental.} pragma to enable 'parallel'")
result = setMs(n, s)
var x = n.lastSon
if x.kind == nkDo: x = x[bodyPos]
inc c.inParallelStmt
result[1] = semStmt(c, x, {})
dec c.inParallelStmt
of mSpawn:
markUsed(c, n.info, s)
when defined(leanCompiler):
result = localErrorNode(c, n, "compiler was built without 'spawn' support")
else:
result = setMs(n, s)
for i in 1..<n.len:
result[i] = semExpr(c, n[i])
if n.len > 1 and n[1].kind notin nkCallKinds:
return localErrorNode(c, n, n[1].info, "'spawn' takes a call expression; got: " & $n[1])
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 c.graph.emptyNode
of mProcCall:
markUsed(c, n.info, s)
result = setMs(n, s)
result[1] = semExpr(c, n[1])
result.typ() = n[1].typ
of mPlugin:
markUsed(c, n.info, s)
# 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[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)
of mRunnableExamples:
markUsed(c, n.info, s)
if c.config.cmd in cmdDocLike and n.len >= 2 and n.lastSon.kind == nkStmtList:
when false:
# some of this dead code was moved to `prepareExamples`
if sfMainModule in c.module.flags:
let inp = toFullPath(c.config, c.module.info)
if c.runnableExamples == nil:
c.runnableExamples = newTree(nkStmtList,
newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
let imports = newTree(nkStmtList)
var savedLastSon = copyTree n.lastSon
extractImports(savedLastSon, imports)
for imp in imports: c.runnableExamples.add imp
c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree savedLastSon)
result = setMs(n, s)
else:
result = c.graph.emptyNode
of mSizeOf:
markUsed(c, n.info, s)
result = semSizeof(c, setMs(n, s))
of mArrToSeq, mOpenArrayToSeq:
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind in {tySequence, tyOpenArray}):
# seq type inference
var arrayType = newType(tyOpenArray, c.idgen, expected.owner)
arrayType.rawAddSon(expected[0])
if n[0].kind == nkSym and sfFromGeneric in n[0].sym.flags:
# may have been resolved to `@`[empty] at some point,
# reset to `@` to deal with this
n[0] = newSymNode(n[0].sym.instantiatedFrom, n[0].info)
n[1] = semExpr(c, n[1], flags, arrayType)
result = semDirectOp(c, n, flags, expectedType)
else:
result = semDirectOp(c, n, flags, expectedType)
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
let flags = if semCheck: {efWantStmt} else: {}
template setResult(e: untyped) =
if semCheck: result = semExpr(c, e, flags) # 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.len in 1..2 and n[0].kind == nkElifBranch and (
n.len == 1 or n[1].kind == nkElse):
var exprNode = n[0][0]
if exprNode.kind == nkOpenSym:
exprNode = exprNode[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
var cannotResolve = false
for i in 0..<n.len:
var it = n[i]
case it.kind
of nkElifBranch, nkElifExpr:
checkSonsLen(it, 2, c.config)
if whenNimvm:
if semCheck:
it[1] = semExpr(c, it[1], flags)
typ = commonType(c, typ, it[1].typ)
result = n # when nimvm is not elimited until codegen
elif c.inGenericContext > 0:
let e = semExprWithType(c, it[0])
if e.typ.kind == tyFromExpr:
it[0] = makeStaticExpr(c, e)
cannotResolve = true
else:
it[0] = forceBool(c, e)
let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
if val == nil or val.kind != nkIntLit:
cannotResolve = true
elif not cannotResolve and val.intVal != 0 and result == nil:
setResult(it[1])
return # we're not in nimvm and we already have a result
else:
let e = forceBool(c, semConstExpr(c, it[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[1])
return # we're not in nimvm and we already have a result
of nkElse, nkElseExpr:
checkSonsLen(it, 1, c.config)
if cannotResolve:
discard
elif result == nil or whenNimvm:
if semCheck:
it[0] = semExpr(c, it[0], flags)
typ = commonType(c, typ, it[0].typ)
if typ != nil and typ.kind != tyUntyped:
it[0] = fitNode(c, typ, it[0], it[0].info)
if result == nil:
result = it[0]
else: illFormedAst(n, c.config)
if cannotResolve:
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
if result == nil:
result = newNodeI(nkEmpty, n.info)
if whenNimvm:
result.typ() = typ
if n.len == 1:
result.add(newTree(nkElse, newNode(nkStmtList)))
proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
result = newNodeI(nkCurly, n.info)
result.typ() = newTypeS(tySet, c)
result.typ.flags.incl tfIsConstructor
var expectedElementType: PType = nil
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind == tySet):
expectedElementType = expected[0]
if n.len == 0:
rawAddSon(result.typ,
if expectedElementType != nil and
typeAllowed(expectedElementType, skLet, c) == nil:
expectedElementType
else:
newTypeS(tyEmpty, c))
else:
# only semantic checking for all elements, later type checking:
var typ: PType = nil
for i in 0..<n.len:
let doSetType = typ == nil
if isRange(n[i]):
checkSonsLen(n[i], 3, c.config)
n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType)
n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType)
if doSetType:
typ = skipTypes(n[i][1].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
n[i].typ() = n[i][2].typ # range node needs type too
elif n[i].kind == nkRange:
# already semchecked
if doSetType:
typ = skipTypes(n[i][0].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
else:
n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
if doSetType:
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
if doSetType:
if not isOrdinalType(typ, allowEnumWithHoles=true):
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
elif isIntLit(typ):
# set of int literal, use a default range smaller than the max range
typ = makeRangeType(c, 0, DefaultSetElements-1, n.info)
elif lengthOrd(c.config, typ) > MaxSetElements:
message(c.config, n.info, warnAboveMaxSizeSet, "type '" &
typeToString(typ, preferDesc) & "' is too big to be a `set` element, " &
"assuming a range of 0.." & $(MaxSetElements - 1) &
", explicitly write this range to get rid of warning")
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
if expectedElementType == nil:
expectedElementType = typ
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
else:
m = fitNode(c, typ, n[i], info)
result.add m
proc semTableConstr(c: PContext, n: PNode; expectedType: PType = nil): 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:
var x = n[i]
if x.kind == nkExprColonExpr and x.len == 2:
for j in lastKey..<i:
var pair = newNodeI(nkTupleConstr, x.info)
pair.add(n[j])
pair.add(x[1])
result.add(pair)
var pair = newNodeI(nkTupleConstr, x.info)
pair.add(x[0])
pair.add(x[1])
result.add(pair)
lastKey = i+1
if lastKey != n.len: illFormedAst(n, c.config)
result = semExpr(c, result, expectedType = expectedType)
type
TParKind = enum
paNone, paSingle, paTupleFields, paTuplePositions
proc checkPar(c: PContext; n: PNode): TParKind =
if n.len == 0:
result = paTuplePositions # ()
elif n.len == 1:
if n[0].kind == nkExprColonExpr: result = paTupleFields
elif n.kind == nkTupleConstr: result = paTuplePositions
else: result = paSingle # (expr)
else:
if n[0].kind == nkExprColonExpr: result = paTupleFields
else: result = paTuplePositions
for i in 0..<n.len:
if result == paTupleFields:
if (n[i].kind != nkExprColonExpr) or
n[i][0].kind notin {nkSym, nkIdent, nkAccQuoted}:
localError(c.config, n[i].info, errNamedExprExpected)
return paNone
else:
if n[i].kind == nkExprColonExpr:
localError(c.config, n[i].info, errNamedExprNotAllowed)
return paNone
proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = newNodeI(nkTupleConstr, n.info)
var expected: PType = nil
if expectedType != nil:
expected = expectedType.skipTypes(abstractRange-{tyDistinct})
if not (expected.kind == tyTuple and expected.len == n.len):
expected = nil
var typ = newTypeS(tyTuple, c)
typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs
var ids = initIntSet()
for i in 0..<n.len:
if n[i].kind != nkExprColonExpr:
illFormedAst(n[i], c.config)
let id = considerQuotedIdent(c, n[i][0])
if containsOrIncl(ids, id.id):
localError(c.config, n[i].info, errFieldInitTwice % id.s)
# can check if field name matches expected type here
let expectedElemType = if expected != nil: expected[i] else: nil
n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType)
if expectedElemType != nil and
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
# hasEmpty/nil check is to not break existing code like
# `const foo = [(1, {}), (2, {false})]`,
# `const foo = if true: (0, nil) else: (1, new(int))`
n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info)
if n[i][1].typ.kind == tyTypeDesc:
localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.")
n[i][1].typ() = errorType(c)
var f = newSymS(skField, n[i][0], c)
f.typ = skipIntLit(n[i][1].typ.skipTypes({tySink}), c.idgen)
f.position = i
rawAddSon(typ, f.typ)
typ.n.add newSymNode(f)
n[i][0] = newSymNode(f)
result.add n[i]
result.typ() = typ
proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = n # we don't modify n, but compute the type:
result.transitionSonsKind(nkTupleConstr)
var expected: PType = nil
if expectedType != nil:
expected = expectedType.skipTypes(abstractRange-{tyDistinct})
if not (expected.kind == tyTuple and expected.len == n.len):
expected = nil
var typ = newTypeS(tyTuple, c) # leave typ.n nil!
for i in 0..<n.len:
let expectedElemType = if expected != nil: expected[i] else: nil
n[i] = semExprWithType(c, n[i], {}, expectedElemType)
if expectedElemType != nil and
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
# hasEmpty/nil check is to not break existing code like
# `const foo = [(1, {}), (2, {false})]`,
# `const foo = if true: (0, nil) else: (1, new(int))`
n[i] = fitNode(c, expectedElemType, n[i], n[i].info)
addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
result.typ() = typ
include semobjconstr
proc semBlock(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
result = n
inc(c.p.nestedBlockCounter)
let oldBreakInLoop = c.p.breakInLoop
c.p.breakInLoop = false
checkSonsLen(n, 2, c.config)
openScope(c) # BUGFIX: label is in the scope of block!
if n[0].kind != nkEmpty:
var labl = newSymG(skLabel, n[0], c)
if sfGenSym notin labl.flags:
addDecl(c, labl)
elif labl.owner == nil:
setOwner(labl, c.p.owner)
n[0] = newSymNode(labl, n[0].info)
suggestSym(c.graph, n[0].info, labl, c.graph.usageSym)
styleCheckDef(c, labl)
onDef(n[0].info, labl)
n[1] = semExpr(c, n[1], flags, expectedType)
n.typ() = n[1].typ
if isEmptyType(n.typ): n.transitionSonsKind(nkBlockStmt)
else: n.transitionSonsKind(nkBlockExpr)
closeScope(c)
c.p.breakInLoop = oldBreakInLoop
dec(c.p.nestedBlockCounter)
proc semExportExcept(c: PContext, n: PNode): PNode =
let moduleName = semExpr(c, n[0])
if moduleName.kind != nkSym or moduleName.sym.kind != skModule:
localError(c.config, n.info, "The export/except syntax expects a module name")
return n
let exceptSet = readExceptSet(c, n)
let exported = moduleName.sym
result = newNodeI(nkExportStmt, n.info)
reexportSym(c, exported)
for s in allSyms(c.graph, exported):
if s.kind in ExportableSymKinds+{skModule} and
s.name.id notin exceptSet and sfError notin s.flags:
reexportSym(c, s)
result.add newSymNode(s, n.info)
markUsed(c, n.info, exported)
proc semExport(c: PContext, n: PNode): PNode =
proc specialSyms(c: PContext; s: PSym) {.inline.} =
if s.kind == skConverter: addConverter(c, LazySym(sym: s))
elif s.kind == skType and s.typ != nil and s.typ.kind == tyEnum and sfPure in s.flags:
addPureEnum(c, LazySym(sym: s))
result = newNodeI(nkExportStmt, n.info)
for i in 0..<n.len:
let a = n[i]
var o: TOverloadIter = default(TOverloadIter)
var s = initOverloadIter(o, c, a)
if s == nil:
localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a))
elif s.kind == skModule:
# forward everything from that module:
reexportSym(c, s)
for it in allSyms(c.graph, s):
if it.kind in ExportableSymKinds+{skModule}:
reexportSym(c, it)
result.add newSymNode(it, a.info)
specialSyms(c, it)
markUsed(c, n.info, s)
else:
while s != nil:
if s.kind == skEnumField:
localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a) &
"; enum field cannot be exported individually")
if s.kind in ExportableSymKinds+{skModule} and sfError notin s.flags:
result.add(newSymNode(s, a.info))
reexportSym(c, s)
markUsed(c, n.info, s)
specialSyms(c, s)
if s.kind == skType and sfPure notin s.flags:
var etyp = s.typ
if etyp.kind in {tyBool, tyEnum}:
for j in 0..<etyp.n.len:
var e = etyp.n[j].sym
if e.kind != skEnumField:
internalError(c.config, s.info, "rawImportSymbol")
reexportSym(c, e)
s = nextOverloadIter(o, c, a)
proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
var tupexp = semTuplePositionsConstr(c, n, flags, expectedType)
var isTupleType: bool = false
if tupexp.len > 0: # don't interpret () as type
isTupleType = tupexp[0].typ.kind == tyTypeDesc
# check if either everything or nothing is tyTypeDesc
for i in 1..<tupexp.len:
if isTupleType != (tupexp[i].typ.kind == tyTypeDesc):
return localErrorNode(c, n, tupexp[i].info, "Mixing types and values in tuples is not allowed.")
if isTupleType: # expressions as ``(int, string)`` are reinterpret as type expressions
result = n
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
result.typ() = makeTypeDesc(c, typ)
else:
result = tupexp
proc isExplicitGenericCall(c: PContext, n: PNode): bool =
## checks if a call node `n` is a routine call with explicit generic params
##
## the callee node needs to be either an nkBracketExpr or a call to a
## symchoice of `[]` in which case it will be transformed into nkBracketExpr
##
## the LHS of the bracket expr has to either be a symchoice or resolve to
## a routine symbol
template checkCallee(n: PNode) =
# check subscript LHS, `n` must be mutable
if isSymChoice(n):
result = true
else:
let s = qualifiedLookUp(c, n, {})
if s != nil and s.kind in routineKinds:
result = true
n = semSymGenericInstantiation(c, n, s)
assert n.kind in nkCallKinds
result = false
let a = n[0]
case a.kind
of nkBracketExpr:
checkCallee(a[0])
of nkCallKinds:
let b = a[0]
if b.kind in nkSymChoices:
let name = b.getPIdent
if name != nil and name.s == "[]":
checkCallee(a[1])
if result:
# transform callee into normal bracket expr, only on success
let be = newNodeI(nkBracketExpr, a.info)
for i in 1..<a.len: be.add(a[i])
n[0] = be
else:
result = false
proc asBracketExpr(c: PContext; n: PNode): PNode =
proc isGeneric(c: PContext; n: PNode): bool =
if n.kind in {nkIdent, nkAccQuoted}:
let s = qualifiedLookUp(c, n, {})
result = s != nil and isGenericRoutineStrict(s)
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
return nil
proc isOpenArraySym(x: PNode): bool =
var x = x
while true:
case x.kind
of {nkAddr, nkHiddenAddr}:
x = x[0]
of {nkHiddenStdConv, nkHiddenDeref}:
x = x[1]
else:
break
result = x.kind == nkSym
proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) =
# This takes care of complicated signatures such as:
# proc foo(a: int, b = a)
# proc bar(a: int, b: int, c = a + b)
#
# The recursion may confuse you. It performs two duties:
#
# 1) extracting all referenced params from default expressions
# into a let section preceding the call
#
# 2) replacing the "references" within the default expression
# with these extracted skLet symbols.
#
# The first duty is carried out directly in the code here, while the second
# duty is activated by returning a non-nil value. The caller is responsible
# for replacing the input to the function with the returned non-nil value.
# (which is the hoisted symbol)
if defExpr.kind == nkSym and defExpr.sym.kind == skParam and
(defExpr.sym.owner == call[0].sym or
# symbol was resolved before proc was instantiated:
(sfFromGeneric in call[0].sym.flags and
defExpr.sym.owner == call[0].sym.instantiatedFrom)):
let paramPos = defExpr.sym.position + 1
if call[paramPos].skipAddr.kind != nkSym and not (
skipTypes(call[paramPos].typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
isOpenArraySym(call[paramPos])
):
let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen,
c.p.owner, letSection.info, c.p.owner.options)
hoistedVarSym.typ = call[paramPos].typ
letSection.add newTreeI(nkIdentDefs, letSection.info,
newSymNode(hoistedVarSym),
newNodeI(nkEmpty, letSection.info),
call[paramPos])
call[paramPos] = newSymNode(hoistedVarSym) # Refer the original arg to its hoisted sym
# arg we refer to is a sym, whether introduced by hoisting or not doesn't matter, we simply reuse it
defExpr = call[paramPos]
else:
for i in 0..<defExpr.safeLen:
hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
proc getNilType(c: PContext): PType =
result = c.nilTypeCache
if result == nil:
result = newTypeS(tyNil, c)
result.size = c.config.target.ptrSize
result.align = c.config.target.ptrSize.int16
c.nilTypeCache = result
proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode =
var o: TOverloadIter = default(TOverloadIter)
var i = 0
var a = initOverloadIter(o, c, n)
while a != nil:
if a.kind == skEnumField:
inc(i)
if i > 1: break
a = nextOverloadIter(o, c, n)
let info = getCallLineInfo(n)
if i <= 1:
if sfGenSym notin s.flags:
result = newSymNode(s, info)
markUsed(c, info, s, efInCall notin flags)
onUse(info, s)
else:
result = n
else:
result = newNodeIT(nkClosedSymChoice, info, newTypeS(tyNone, c))
a = initOverloadIter(o, c, n)
while a != nil:
if a.kind == skEnumField:
incl(a.flags, sfUsed)
markOwnerModuleAsUsed(c, a)
result.add newSymNode(a, info)
onUse(info, a)
a = nextOverloadIter(o, c, n)
proc semPragmaStmt(c: PContext; n: PNode) =
if c.p.owner.kind == skModule:
pragma(c, c.p.owner, n, stmtPragmas+stmtPragmasTopLevel, true)
else:
pragma(c, c.p.owner, n, stmtPragmas, true)
proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode,
flags: TExprFlags, expectedType: PType): PSym =
# result is nil on error or if a node that can't produce a sym is resolved
let ident = considerQuotedIdent(c, n)
var filter = {low(TSymKind)..high(TSymKind)}
if efNoEvaluateGeneric in flags or expectedType != nil:
# `a[...]` where `a` is a module or package is not possible
filter.excl {skModule, skPackage}
let includePureEnum = expectedType != nil and
expectedType.skipTypes(abstractRange-{tyDistinct}).kind == tyEnum
let candidates = lookUpCandidates(c, ident, filter,
includePureEnum = includePureEnum)
if candidates.len == 0:
result = errorUndeclaredIdentifierHint(c, ident, n.info)
elif candidates.len == 1 or {efNoEvaluateGeneric, efInCall} * flags != {}:
# unambiguous, or we don't care about ambiguity
result = candidates[0]
else:
# ambiguous symbols have 1 last chance as a symchoice
var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c))
for cand in candidates:
case cand.kind
of skModule, skPackage:
discard
of skType:
choice.add newSymNodeTypeDesc(cand, c.idgen, n.info)
else:
choice.add newSymNode(cand, n.info)
if choice.len == 0:
# we know candidates.len > 1, we just couldn't put any in a symchoice
errorUseQualifier(c, n.info, candidates)
return nil
resolveSymChoice(c, choice, flags, expectedType)
# choice.len == 1 can be true here but as long as it's a symchoice
# it's still not resolved
if isSymChoice(choice):
result = nil
if efAllowSymChoice in flags:
resultNode = choice
else:
errorUseQualifier(c, n.info, candidates)
else:
if choice.kind == nkSym:
result = choice.sym
else:
# resolution could have generated nkHiddenStdConv etc
resultNode = semExpr(c, choice, flags, expectedType)
result = nil
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
when defined(nimCompilerStacktraceHints):
setFrameMsg c.config$n.info & " " & $n.kind
when false: # see `tdebugutils`
if isCompilerDebug():
echo (">", c.config$n.info, n, flags, n.kind)
defer:
if isCompilerDebug():
echo ("<", c.config$n.info, n, ?.result.typ)
template directLiteral(typeKind: TTypeKind) =
if result.typ == nil:
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind == typeKind):
result.typ() = expected
changeType(c, result, expectedType, check=true)
else:
result.typ() = getSysType(c.graph, n.info, typeKind)
result = n
when defined(nimsuggest):
var expandStarted = false
if c.config.ideCmd == ideExpand and not c.config.expandProgress and
((n.kind in {nkFuncDef, nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkConverterDef} and
n.info.exactEquals(c.config.expandPosition)) or
(n.kind in {nkCall, nkCommand} and
n[0].info.exactEquals(c.config.expandPosition))):
expandStarted = true
c.config.expandProgress = true
if c.config.expandLevels == 0:
c.config.expandNodeResult = $n
suggestQuit()
if c.config.cmd == cmdIdeTools: suggestExpr(c, n)
if nfSem in n.flags: return
case n.kind
of nkIdent, nkAccQuoted:
let s = resolveIdentToSym(c, n, result, flags, expectedType)
if s == nil:
# resolveIdentToSym either errored or gave a result node
return
if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
case s.kind
of skProc, skFunc, 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(c.config, n.info, errInstantiateXExplicitly, s.name.s)
# "procs literals" are 'owned'
if optOwnedRefs in c.config.globalOptions:
result.typ() = makeVarType(c, result.typ, tyOwned)
of skEnumField:
result = enumFieldSymChoice(c, n, s, flags)
else:
result = semSym(c, n, s, flags)
if isSymChoice(result):
result = semSymChoice(c, result, flags, expectedType)
of nkClosedSymChoice, nkOpenSymChoice:
result = semSymChoice(c, n, flags, expectedType)
of nkSym:
let s = n.sym
if nfDisabledOpenSym in n.flags:
let override = genericsOpenSym in c.features
let res = semOpenSym(c, n, flags, expectedType,
warnDisabled = not override)
if res != nil:
assert override
return res
# 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, s, flags)
of nkOpenSym:
assert n.len == 1
let inner = n[0]
result = semOpenSym(c, inner, flags, expectedType)
of nkEmpty, nkNone, nkCommentStmt, nkType:
discard
of nkNilLit:
if result.typ == nil:
result.typ() = getNilType(c)
if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}:
var m = newCandidate(c, result.typ)
if typeRel(m, expectedType, result.typ) >= isSubtype:
result.typ() = expectedType
# or: result = fitNode(c, expectedType, result, n.info)
of nkIntLit:
if result.typ == nil:
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind in {tyInt..tyInt64,
tyUInt..tyUInt64,
tyFloat..tyFloat128}):
if expected.kind in {tyFloat..tyFloat128}:
n.transitionIntToFloatKind(nkFloatLit)
changeType(c, result, expectedType, check=true)
else:
setIntLitType(c, result)
of nkInt8Lit: directLiteral(tyInt8)
of nkInt16Lit: directLiteral(tyInt16)
of nkInt32Lit: directLiteral(tyInt32)
of nkInt64Lit: directLiteral(tyInt64)
of nkUIntLit: directLiteral(tyUInt)
of nkUInt8Lit: directLiteral(tyUInt8)
of nkUInt16Lit: directLiteral(tyUInt16)
of nkUInt32Lit: directLiteral(tyUInt32)
of nkUInt64Lit: directLiteral(tyUInt64)
of nkFloatLit:
if result.typ == nil:
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind in {tyFloat..tyFloat128}):
result.typ() = expected
changeType(c, result, expectedType, check=true)
else:
result.typ() = getSysType(c.graph, n.info, tyFloat64)
of nkFloat32Lit: directLiteral(tyFloat32)
of nkFloat64Lit: directLiteral(tyFloat64)
of nkFloat128Lit: directLiteral(tyFloat128)
of nkStrLit..nkTripleStrLit:
if result.typ == nil:
if expectedType != nil and (
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
expected.kind in {tyString, tyCstring}):
result.typ() = expectedType
else:
result.typ() = getSysType(c.graph, n.info, tyString)
of nkCharLit: directLiteral(tyChar)
of nkDotExpr:
result = semFieldAccess(c, n, flags)
if result.kind == nkDotCall:
result.transitionSonsKind(nkCall)
result = semExpr(c, result, flags, expectedType)
of nkBind:
message(c.config, n.info, warnDeprecated, "bind is deprecated")
result = semExpr(c, n[0], flags, expectedType)
of nkTypeOfExpr..nkTupleClassTy, nkStaticTy, nkRefTy..nkEnumTy:
if c.matchedConcept != nil and n.len == 1:
let modifier = n.modifierTypeKindOfNode
if modifier != tyNone:
var baseType = semExpr(c, n[0]).typ.skipTypes({tyTypeDesc})
result.typ() = c.makeTypeDesc(newTypeS(modifier, c, baseType))
return
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
result.typ() = makeTypeDesc(c, typ)
of nkStmtListType:
let typ = semTypeNode(c, n, nil)
result.typ() = makeTypeDesc(c, typ)
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1, c.config)
#when defined(nimsuggest):
# if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n)
let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
c.isAmbiguous = false
var s = qualifiedLookUp(c, n[0], mode)
if s != nil:
case s.kind
of skMacro, skTemplate:
result = semDirectOp(c, n, flags, expectedType)
of skType:
# XXX think about this more (``set`` procs)
let ambig = c.isAmbiguous
if not (n[0].kind in nkSymChoices + {nkIdent, nkDotExpr} and ambig) and n.len == 2:
result = semConv(c, n, flags, expectedType)
elif n.len == 1:
if ambig:
errorUseQualifier(c, n.info, s)
else:
result = semObjConstr(c, n, flags, expectedType)
elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
else: result = semMagic(c, n, s, flags, expectedType)
of skProc, skFunc, skMethod, skConverter, skIterator:
if s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
else: result = semMagic(c, n, s, flags, expectedType)
else:
#liMessage(n.info, warnUser, renderTree(n));
result = semIndirectOp(c, n, flags, expectedType)
elif isExplicitGenericCall(c, n): # this modifies `n` if true
result = semDirectOp(c, n, flags, expectedType)
elif nfDotField in n.flags:
result = semDirectOp(c, n, flags, expectedType)
elif isSymChoice(n[0]):
let b = asBracketExpr(c, n)
if b != nil:
result = semExpr(c, b, flags, expectedType)
else:
result = semDirectOp(c, n, flags, expectedType)
else:
result = semIndirectOp(c, n, flags, expectedType)
if nfDefaultRefsParam in result.flags:
result = result.copyTree #XXX: Figure out what causes default param nodes to be shared.. (sigmatch bug?)
# We've found a default value that references another param.
# See the notes in `hoistParamsUsedInDefault` for more details.
var hoistedParams = newNodeI(nkLetSection, result.info)
for i in 1..<result.len:
hoistParamsUsedInDefault(c, result, hoistedParams, result[i])
result = newTreeIT(nkStmtListExpr, result.info, result.typ, hoistedParams, result)
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, expectedType)
of nkBracketExpr:
checkMinSonsLen(n, 1, c.config)
result = semArrayAccess(c, n, flags, expectedType)
of nkCurlyExpr:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags, expectedType)
of nkPragmaExpr:
var
pragma = n[1]
pragmaName = considerQuotedIdent(c, pragma[0])
flags = flags
finalNodeFlags: TNodeFlags = {}
case whichKeyword(pragmaName)
of wExplain:
flags.incl efExplain
of wExecuteOnReload:
finalNodeFlags.incl nfExecuteOnReload
else:
# what other pragmas are allowed for expressions? `likely`, `unlikely`
invalidPragma(c, n)
result = semExpr(c, n[0], flags)
result.flags.incl finalNodeFlags
of nkPar, nkTupleConstr:
case checkPar(c, n)
of paNone: result = errorNode(c, n)
of paTuplePositions: result = semTupleConstr(c, n, flags, expectedType)
of paTupleFields: result = semTupleFieldsConstr(c, n, flags, expectedType)
of paSingle: result = semExpr(c, n[0], flags, expectedType)
of nkCurly: result = semSetConstr(c, n, expectedType)
of nkBracket:
result = semArrayConstr(c, n, flags, expectedType)
of nkObjConstr: result = semObjConstr(c, n, flags, expectedType)
of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags)
of nkDerefExpr: result = semDeref(c, n, flags)
of nkAddr:
result = n
checkSonsLen(n, 1, c.config)
result = semAddr(c, n[0])
of nkHiddenAddr, nkHiddenDeref:
checkSonsLen(n, 1, c.config)
n[0] = semExpr(c, n[0], flags, expectedType)
of nkCast: result = semCast(c, n)
of nkIfExpr, nkIfStmt: result = semIf(c, n, flags, expectedType)
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv:
checkSonsLen(n, 2, c.config)
considerGenSyms(c, n)
of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv:
checkSonsLen(n, 1, c.config)
considerGenSyms(c, n)
of nkChckRangeF, nkChckRange64, nkChckRange:
checkSonsLen(n, 3, c.config)
considerGenSyms(c, n)
of nkCheckedFieldExpr:
checkMinSonsLen(n, 2, c.config)
considerGenSyms(c, n)
of nkTableConstr:
result = semTableConstr(c, n, expectedType)
of nkStaticExpr: result = semStaticExpr(c, n[0], expectedType)
of nkAsgn, nkFastAsgn: result = semAsgn(c, n)
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags, expectedType)
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags, expectedType)
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, flags)
of nkTryStmt, nkHiddenTryStmt: result = semTry(c, n, flags, expectedType)
of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
of nkForStmt, nkParForStmt: result = semFor(c, n, flags)
of nkCaseStmt: result = semCase(c, n, flags, expectedType)
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: semPragmaStmt(c, n)
of nkIteratorDef: result = semIterator(c, n)
of nkProcDef: result = semProc(c, n)
of nkFuncDef: result = semFunc(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:
# this particular way allows 'import' in a 'compiles' context so that
# template canImport(x): bool =
# compiles:
# import x
#
# works:
if c.currentScope.depthLevel > 2 + c.compilesContextId:
localError(c.config, n.info, errXOnlyAtModuleScope % "import")
result = evalImport(c, n)
of nkImportExceptStmt:
if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "import")
result = evalImportExcept(c, n)
of nkFromStmt:
if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "from")
result = evalFrom(c, n)
of nkIncludeStmt:
#if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "include")
result = evalInclude(c, n)
of nkExportStmt:
if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export")
result = semExport(c, n)
of nkExportExceptStmt:
if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export")
result = semExportExcept(c, n)
of nkPragmaBlock:
result = semPragmaBlock(c, n, expectedType)
of nkStaticStmt:
result = semStaticStmt(c, n)
of nkDefer:
if c.currentScope == c.topLevelScope:
localError(c.config, n.info, "defer statement not supported at top level")
openScope(c)
n[0] = semExpr(c, n[0])
closeScope(c)
if not n[0].typ.isEmptyType and not implicitlyDiscardable(n[0]):
localError(c.config, n.info, "'defer' takes a 'void' expression")
#localError(c.config, n.info, errGenerated, "'defer' not allowed in this context")
of nkGotoState, nkState:
if n.len != 1 and n.len != 2: illFormedAst(n, c.config)
for i in 0..<n.len:
n[i] = semExpr(c, n[i])
of nkComesFrom: discard "ignore the comes from information for now"
of nkMixinStmt: discard
of nkBindStmt:
if c.p != nil:
if n.len > 0 and n[0].kind == nkSym:
c.p.localBindStmts.add n
else:
localError(c.config, n.info, "invalid context for 'bind' statement: " &
renderTree(n, {renderNoComments}))
else:
localError(c.config, n.info, "invalid expression: " &
renderTree(n, {renderNoComments}))
if result != nil: incl(result.flags, nfSem)
when defined(nimsuggest):
if expandStarted:
c.config.expandNodeResult = $result
suggestQuit()