mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
refs https://forum.nim-lang.org/t/12785, refs #4711
The code was already there that when `propertyWriteAccess` returns `nil`
(i.e. cannot find a setter), `semAsgn` turns the [LHS into a call and
semchecks
it](1ef9a656d2/compiler/semexprs.nim (L1941-L1948)),
meaning if a setter cannot be found a getter will be assigned to
instead. However `propertyWriteAccess` never returned nil, because
`semOverloadedCallAnalyseEffects` was not called with `efNoUndeclared`
and so produced an error directly. So `efNoUndeclared` is passed to this
call so this code works as intended.
This fixes the issue described in #4711 which was closed because
subscripts do not have the same behavior implemented. However we can
implement this for subscripts as well (I have an implementation ready),
it just changes the error message from the failed overloads of `[]=` to
the failed overloads of `[]` for the LHS, which might be misleading but
is consistent with the error messages for any other assignment. I can do
this in this PR or another one.
3599 lines
136 KiB
Nim
3599 lines
136 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"
|
|
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 or n.typ.kind == tyFromExpr
|
|
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
|
|
var isGeneric = false
|
|
if yy.typ != nil and yy.typ.kind == tyFromExpr:
|
|
isGeneric = true
|
|
typ = nil # will not be used
|
|
elif constructType:
|
|
typ = yy.typ
|
|
if expectedElementType == nil:
|
|
expectedElementType = typ
|
|
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 xx.typ != nil and xx.typ.kind == tyFromExpr:
|
|
isGeneric = true
|
|
elif constructType:
|
|
typ = commonType(c, typ, xx.typ)
|
|
#n[i] = semExprWithType(c, x, {})
|
|
#result.add fitNode(c, typ, n[i])
|
|
inc(lastIndex)
|
|
if isGeneric:
|
|
for i in 0..<result.len:
|
|
if isIntLit(result[i].typ):
|
|
# generic instantiation strips int lit type which makes conversions fail
|
|
result[i].typ() = nil
|
|
result.typ() = nil # current result.typ is invalid, index type is nil
|
|
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
|
return
|
|
if constructType:
|
|
addSonSkipIntLit(result.typ, typ, c.idgen)
|
|
for i in 0..<result.len:
|
|
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
|
|
|
|
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 hiddenDerefDepth(n: PNode): int =
|
|
result = 0
|
|
var n = n
|
|
while n.kind == nkHiddenDeref:
|
|
inc result
|
|
n = n[0]
|
|
|
|
proc dotTransformation(c: PContext, n: PNode, initialDerefs: int): PNode =
|
|
var root = n[0]
|
|
let currentDerefs = hiddenDerefDepth(root)
|
|
if currentDerefs > initialDerefs:
|
|
# hidden derefs were inserted by `builtinFieldAccess` for fields of
|
|
# `ref object` etc.
|
|
# undo the derefs for overload resolution
|
|
for _ in initialDerefs ..< currentDerefs:
|
|
root = root[0]
|
|
root = copyTree(root)
|
|
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 root
|
|
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 root
|
|
|
|
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}
|
|
let initialDerefDepth = hiddenDerefDepth(n[0])
|
|
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, initialDerefDepth)
|
|
|
|
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, {efNoUndeclared})
|
|
|
|
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}
|
|
let initialDerefDepth = hiddenDerefDepth(a[0])
|
|
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], initialDerefDepth)
|
|
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, nkOpenSym:
|
|
result = n[0].sym
|
|
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
|
|
var isGeneric = false
|
|
for i in 0..<n.len:
|
|
let doSetType = typ == nil
|
|
if isRange(n[i]):
|
|
checkSonsLen(n[i], 3, c.config)
|
|
n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType)
|
|
n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType)
|
|
if (n[i][1].typ != nil and n[i][1].typ.kind == tyFromExpr) or
|
|
(n[i][2].typ != nil and n[i][2].typ.kind == tyFromExpr):
|
|
isGeneric = true
|
|
else:
|
|
if doSetType:
|
|
typ = skipTypes(n[i][1].typ,
|
|
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
|
n[i].typ() = n[i][2].typ # range node needs type too
|
|
elif n[i].kind == nkRange:
|
|
# already semchecked
|
|
if doSetType:
|
|
typ = skipTypes(n[i][0].typ,
|
|
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
|
else:
|
|
n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
|
|
if n[i].typ != nil and n[i].typ.kind == tyFromExpr:
|
|
isGeneric = true
|
|
elif doSetType:
|
|
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
|
if doSetType and not isGeneric:
|
|
if not isOrdinalType(typ, allowEnumWithHoles=true):
|
|
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
|
|
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
|
|
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
|
|
if isGeneric:
|
|
for i in 0..<n.len:
|
|
if isIntLit(n[i].typ):
|
|
# generic instantiation strips int lit type which makes conversions fail
|
|
n[i].typ() = nil
|
|
result.add n[i]
|
|
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
|
return
|
|
addSonSkipIntLit(result.typ, typ, c.idgen)
|
|
for i in 0..<n.len:
|
|
var m: PNode
|
|
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()
|
|
var isGeneric = false
|
|
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 n[i][1].typ != nil and n[i][1].typ.kind == tyFromExpr:
|
|
isGeneric = true
|
|
elif expectedElemType != nil and
|
|
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
|
|
# hasEmpty/nil check is to not break existing code like
|
|
# `const foo = [(1, {}), (2, {false})]`,
|
|
# `const foo = if true: (0, nil) else: (1, new(int))`
|
|
let conversion = indexTypesMatch(c, expectedElemType, n[i][1].typ, n[i][1])
|
|
# ignore matching error, full tuple will be matched later which may call converter, see #24609
|
|
if conversion != nil:
|
|
n[i][1] = conversion
|
|
|
|
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]
|
|
if isGeneric:
|
|
for i in 0..<result.len:
|
|
if isIntLit(result[i][1].typ):
|
|
# generic instantiation strips int lit type which makes conversions fail
|
|
result[i][1].typ() = nil
|
|
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
|
return
|
|
let oldType = n.typ
|
|
result.typ() = typ
|
|
if oldType != nil and not hasEmpty(oldType): # see hasEmpty comment above
|
|
# convert back to old type
|
|
let conversion = indexTypesMatch(c, oldType, typ, result)
|
|
# ignore matching error, the goal is just to keep the original type info
|
|
if conversion != nil:
|
|
result.typ() = oldType
|
|
|
|
proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
|
result = n # we don't modify n, but compute the type:
|
|
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!
|
|
var isGeneric = false
|
|
for i in 0..<n.len:
|
|
let expectedElemType = if expected != nil: expected[i] else: nil
|
|
n[i] = semExprWithType(c, n[i], {}, expectedElemType)
|
|
if n[i].typ != nil and n[i].typ.kind == tyFromExpr:
|
|
isGeneric = true
|
|
elif expectedElemType != nil and
|
|
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
|
|
# hasEmpty/nil check is to not break existing code like
|
|
# `const foo = [(1, {}), (2, {false})]`,
|
|
# `const foo = if true: (0, nil) else: (1, new(int))`
|
|
let conversion = indexTypesMatch(c, expectedElemType, n[i].typ, n[i])
|
|
# ignore matching error, full tuple will be matched later which may call converter, see #24609
|
|
if conversion != nil:
|
|
n[i] = conversion
|
|
addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
|
|
if isGeneric:
|
|
for i in 0..<result.len:
|
|
if isIntLit(result[i].typ):
|
|
# generic instantiation strips int lit type which makes conversions fail
|
|
result[i].typ() = nil
|
|
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
|
return
|
|
let oldType = n.typ
|
|
result.typ() = typ
|
|
if oldType != nil and not hasEmpty(oldType): # see hasEmpty comment above
|
|
# convert back to old type
|
|
let conversion = indexTypesMatch(c, oldType, typ, result)
|
|
# ignore matching error, the goal is just to keep the original type info
|
|
if conversion != nil:
|
|
result.typ() = oldType
|
|
|
|
include semobjconstr
|
|
|
|
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 =
|
|
result = semTuplePositionsConstr(c, n, flags, expectedType)
|
|
if result.typ.kind == tyFromExpr:
|
|
# tyFromExpr is already ambivalent between types and values
|
|
return
|
|
var tupexp = result
|
|
while tupexp.kind == nkHiddenSubConv: tupexp = tupexp[1]
|
|
var isTupleType: bool = false
|
|
if tupexp.len > 0: # don't interpret () as type
|
|
internalAssert c.config, tupexp.kind == nkTupleConstr
|
|
isTupleType = tupexp[0].typ.kind == tyTypeDesc
|
|
# check if either everything or nothing is tyTypeDesc
|
|
for i in 1..<tupexp.len:
|
|
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)
|
|
|
|
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()
|