diff options
Diffstat (limited to 'compiler/semexprs.nim')
-rw-r--r-- | compiler/semexprs.nim | 3254 |
1 files changed, 2184 insertions, 1070 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9d7c493a7..2885142a7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -10,12 +10,15 @@ # this module does the semantic checking for expressions # included from sem.nim +when defined(nimCompilerStacktraceHints): + import std/stackframes + const errExprXHasNoType = "expression '$1' has no type (or is ambiguous)" errXExpectsTypeOrValue = "'$1' expects a type or value" errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable" errXStackEscape = "address of '$1' may not escape its stack frame" - errExprHasNoAddress = "expression has no address; maybe use 'unsafeAddr'" + errExprHasNoAddress = "expression has no address" errCannotInterpretNodeX = "cannot evaluate '$1'" errNamedExprExpected = "named expression expected" errNamedExprNotAllowed = "named expression not allowed here" @@ -23,30 +26,34 @@ const errUndeclaredFieldX = "undeclared field: '$1'" proc semTemplateExpr(c: PContext, n: PNode, s: PSym, - flags: TExprFlags = {}): PNode = - markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) - pushInfoContext(c.config, n.info) - result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags) - if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) + 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 = n.info + 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}) - #if result.kind == nkEmpty and result.typ.isNil: - # do not produce another redundant error message: - #raiseRecoverableError("") - # result = errorNode(c, n) + result = semExpr(c, n, flags + {efOperand, efAllowSymChoice}) if result.typ != nil: - # XXX tyGenericInst here? - if result.typ.kind == tyProc and tfUnresolved in result.typ.flags: - localError(c.config, n.info, errProcHasNoConcreteType % n.renderTree) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) elif {efWantStmt, efAllowStmt} * flags != {}: result.typ = newTypeS(tyVoid, c) @@ -55,24 +62,51 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = renderTree(result, {renderNoComments})) result.typ = errorType(c) -proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = - result = semExpr(c, n, flags+{efWantValue}) - if result.isNil or result.kind == nkEmpty: +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: - #raiseRecoverableError("") result = errorNode(c, n) - if result.typ == nil or result.typ == c.enforceVoidContext: + +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 = semExpr(c, n, flags) - if result.kind == nkEmpty: - # do not produce another redundant error message: - result = errorNode(c, n) + result = semExprCheck(c, n, flags) if result.typ == nil: localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) @@ -81,8 +115,114 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = 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.ast) + result = copyTree(s.astdef) if result.isNil: localError(c.config, n.info, "constant of type '" & typeToString(s.typ) & "' has no value") result = newSymNode(s) @@ -94,7 +234,8 @@ type TConvStatus = enum convOK, convNotNeedeed, - convNotLegal + convNotLegal, + convNotInRange proc checkConversionBetweenObjects(castDest, src: PType; pointers: int): TConvStatus = let diff = inheritanceDiff(castDest, src) @@ -106,44 +247,79 @@ proc checkConversionBetweenObjects(castDest, src: PType; pointers: int): TConvSt const IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64} -proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = +proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = + let srcTyp = src.typ.skipTypes({tyStatic}) result = convOK - if sameType(castDest, src) and castDest.sym == src.sym: + if sameType(targetTyp, srcTyp) and targetTyp.sym == srcTyp.sym: # don't annoy conversions that may be needed on another processor: - if castDest.kind notin IntegralTypes+{tyRange}: + if targetTyp.kind notin IntegralTypes+{tyRange}: result = convNotNeedeed return - var d = skipTypes(castDest, abstractVar) - var s = src + var d = skipTypes(targetTyp, abstractVar) + var s = srcTyp if s.kind in tyUserTypeClasses and s.isResolvedUserTypeClass: - s = s.lastSon - s = skipTypes(s, abstractVar-{tyTypeDesc}) + 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}) and (d.kind == s.kind): - d = d.lastSon - s = s.lastSon + 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.kind == tyObject and s.kind == tyObject: - result = checkConversionBetweenObjects(d, s, pointers) - elif (skipTypes(castDest, abstractVarRange).kind in IntegralTypes) and - (skipTypes(src, abstractVarRange-{tyTypeDesc}).kind in IntegralTypes): - # accept conversion between integral types - discard + 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(castDest, src, dcEqIgnoreDistinct): + if not compareTypes(targetTyp.skipTypes(abstractVar), srcTyp.skipTypes({tyOwned}), dcEqIgnoreDistinct): result = convNotLegal else: discard -proc isCastable(conf: ConfigRef; dst, src: PType): bool = +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 - ## castDest.size >= src.size, and typeAllowed(dst, skParam) + ## dst.size >= src.size, and typeAllowed(dst, skParam) #const # castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString, # tySequence, tyPointer, tyNil, tyOpenArray, @@ -153,27 +329,38 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool = 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: - result = false + return false elif srcSize < 0: - result = false - elif typeAllowed(dst, skParam) != nil: - result = false + return false + elif typeAllowed(dst, skParam, c, {taIsCastable}) != nil: + return false elif dst.kind == tyProc and dst.callConv == ccClosure: - result = src.kind == tyProc and src.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: - result = dst.size <= conf.target.ptrSize - -proc isSymChoice(n: PNode): bool {.inline.} = - result = n.kind in nkSymChoices + return dst.size <= conf.target.ptrSize proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = # XXX: liftParamType started to perform addDecl @@ -185,101 +372,145 @@ proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = closeScope(c) if lifted != nil: t = lifted -proc semConv(c: PContext, n: PNode): PNode = - if sonsLen(n) != 2: +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.sons[0], nil).skipTypes({tyTypeDesc}) + + 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}: - let baseType = semTypeNode(c, n.sons[1], nil).skipTypes({tyTypeDesc}) - let t = newTypeS(targetType.kind, c) - t.rawAddSonNoPropagationOfTypeFlags baseType + 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.addSon copyTree(n.sons[0]) + 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 contruction uses ':', not '='") - var op = semExprWithType(c, n.sons[1]) - if targetType.isMetaType: + 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.addSon final + 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. - addSon(result, op) + 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.typ) + let status = checkConvertible(c, result.typ, op) case status of convOK: # handle SomeProcType(SomeGenericProc) if op.kind == nkSym and op.sym.isGenericRoutine: - result.sons[1] = fitNode(c, result.typ, result.sons[1], result.info) + 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: - message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) + if efNoSem2Check notin flags: + message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) of convNotLegal: - result = fitNode(c, result.typ, result.sons[1], result.info) + 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 countup(0, sonsLen(op) - 1): - let it = op.sons[i] - let status = checkConvertible(c, result.typ, it.typ) + for i in 0..<op.len: + let it = op[i] + let status = checkConvertible(c, result.typ, it) if status in {convOK, convNotNeedeed}: - markUsed(c.config, n.info, it.sym, c.graph.usageSym) - styleCheckUse(n.info, it.sym) + markUsed(c, n.info, it.sym) + onUse(n.info, it.sym) markIndirect(c, it.sym) return it - errorUseQualifier(c, n.info, op.sons[0].sym) + 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.sons[0], nil) - let castedExpr = semExprWithType(c, n.sons[1]) + 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.sons[0].info, "cannot cast to a non concrete type: '$1'" % $targetType) - if not isCastable(c.config, targetType, castedExpr.typ): - let tar = $targetType - let alt = typeToString(targetType, preferDesc) - let msg = if tar != alt: tar & "=" & alt else: tar - localError(c.config, n.info, "expression cannot be cast to " & msg) + 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 - addSon(result, copyTree(n.sons[0])) - addSon(result, castedExpr) + 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 sonsLen(n) != 2: + if n.len != 2: localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m]) else: - n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) - var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc}) + 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: + of tySequence, tyString, tyCstring, tyOpenArray, tyVarargs: n.typ = getSysType(c.graph, n.info, tyInt) of tyArray: - n.typ = typ.sons[0] # indextype - of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32: - # do not skip the range! - n.typ = n.sons[1].typ.skipTypes(abstractVar) + 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 @@ -289,104 +520,157 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = localError(c.config, n.info, "invalid argument for: " & opToStr[m]) result = n -proc semSizeof(c: PContext, n: PNode): PNode = - if sonsLen(n) != 2: - localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof") - else: - n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) - #restoreOldStyleType(n.sons[1]) - n.typ = getSysType(c.graph, n.info, tyInt) - 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.sonsLen == 3 and - n[1].typ != nil and n[1].typ.kind == tyTypeDesc and + internalAssert c.config, + n.len == 3 and + n[1].typ != nil and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - let t1 = n[1].typ.skipTypes({tyTypeDesc}) + 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) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator notin t.flags)) + 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: - result = newIntNode(nkIntLit, 0) + res = false else: - var rhsOrigType = n[2].typ - var t2 = rhsOrigType.skipTypes({tyTypeDesc}) - maybeLiftType(t2, c, n.info) - var m: TCandidate - initCandidate(c, m, t2) + 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 - let match = typeRel(m, t2, t1) >= isSubtype # isNone - result = newIntNode(nkIntLit, ord(match)) + 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 sonsLen(n) != 3: + 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 = getSysType(c.graph, n.info, tyBool) + n.typ = boolType + var liftLhs = true - n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) + n[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) if n[2].kind notin {nkStrLit..nkTripleStrLit}: let t2 = semTypeNode(c, n[2], nil) - n.sons[2] = newNodeIT(nkType, n[2].info, t2) + 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]) - let lhsType = n[1].typ + var lhsType = n[1].typ if lhsType.kind != tyTypeDesc: - n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info) - elif lhsType.base.kind == tyNone: - # this is a typedesc variable, leave for evals - return + 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 - # BUGFIX: don't evaluate this too early: ``T is void`` - if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags) + result = isOpImpl(c, n, flags) proc semOpAux(c: PContext, n: PNode) = - const flags = {efDetermineType} - for i in countup(1, n.sonsLen-1): - var a = n.sons[i] - if a.kind == nkExprEqExpr and sonsLen(a) == 2: - let info = a.sons[0].info - a.sons[0] = newIdentNode(considerQuotedIdent(c, a.sons[0], a), info) - a.sons[1] = semExprWithType(c, a.sons[1], flags) - a.typ = a.sons[1].typ + 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.sons[i] = semExprWithType(c, a, flags) + 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, "()") - if searchInScopes(c, par) == nil: + var amb = false + if searchInScopes(c, par, amb) == nil: result = nil else: result = newNodeI(nkCall, n.info) - addSon(result, newIdentNode(par, n.info)) - for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i]) - result = semExpr(c, result) + 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, nkBracket: - for i in countup(0, sonsLen(n) - 1): - changeType(c, n.sons[i], elemType(newType), check) + 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}) + 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 sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: + elif n.len > 0 and n[0].kind == nkExprColonExpr: # named tuple? - for i in countup(0, sonsLen(n) - 1): - var m = n.sons[i].sons[0] + for i in 0..<n.len: + var m = n[i][0] if m.kind != nkSym: globalError(c.config, m.info, "invalid tuple constructor") return @@ -395,106 +679,212 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = if f == nil: globalError(c.config, m.info, "unknown identifier: " & m.sym.name.s) return - changeType(c, n.sons[i].sons[1], f.typ, check) + changeType(c, n[i][1], f.typ, check) else: - changeType(c, n.sons[i].sons[1], tup.sons[i], check) + changeType(c, n[i][1], tup[i], check) else: - for i in countup(0, sonsLen(n) - 1): - changeType(c, n.sons[i], tup.sons[i], check) + for i in 0..<n.len: + changeType(c, n[i], tup[i], check) when false: - var m = n.sons[i] - var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i]) - addSon(a, newSymNode(newType.n.sons[i].sym)) - addSon(a, m) - changeType(m, tup.sons[i], check) + 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: + 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 " & typeToString(newType)) + " 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 sonsLen(n) == 0: + if n.len == 0: rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype! else: - var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) - addSonSkipIntLit(typ, t) - typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info) + 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): PNode = +proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = newNodeI(nkBracket, n.info) - result.typ = newTypeS(tyArray, c) - rawAddSon(result.typ, nil) # index type - if sonsLen(n) == 0: - rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype! + # nkBracket nodes can also be produced by the VM as seq constant nodes + # in which case, we cannot produce a new array type for the node, + # as this might lose type info even when the node has array type + let constructType = n.typ.isNil + var expectedElementType, expectedIndexType: PType = nil + var expectedBase: PType = nil + if constructType: + result.typ = newTypeS(tyArray, c) + rawAddSon(result.typ, nil) # index type + if expectedType != nil: + expectedBase = expectedType.skipTypes(abstractRange-{tyDistinct}) + else: + result.typ = n.typ + expectedBase = n.typ.skipTypes(abstractRange) # include tyDistinct this time + if expectedBase != nil: + case expectedBase.kind + of tyArray: + expectedIndexType = expectedBase[0] + expectedElementType = expectedBase[1] + of tyOpenArray, tySequence: + # typed bracket expressions can also have seq type + expectedElementType = expectedBase[0] + else: discard + var + firstIndex, lastIndex: Int128 = Zero + indexType = getSysType(c.graph, n.info, tyInt) + lastValidIndex = lastOrd(c.config, indexType) + if n.len == 0: + if constructType: + rawAddSon(result.typ, + if expectedElementType != nil and + typeAllowed(expectedElementType, skLet, c) == nil: + expectedElementType + else: + newTypeS(tyEmpty, c)) # needs an empty basetype! + lastIndex = toInt128(-1) else: - var x = n.sons[0] - var lastIndex: BiggestInt = 0 - var indexType = getSysType(c.graph, n.info, tyInt) - if x.kind == nkExprColonExpr and sonsLen(x) == 2: - var idx = semConstExpr(c, x.sons[0]) - lastIndex = getOrdValue(idx) - indexType = idx.typ - x = x.sons[1] - - let yy = semExprWithType(c, x) - var typ = yy.typ - addSon(result, yy) - #var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal}) - for i in countup(1, sonsLen(n) - 1): - x = n.sons[i] - if x.kind == nkExprColonExpr and sonsLen(x) == 2: - var idx = semConstExpr(c, x.sons[0]) + var x = n[0] + if x.kind == nkExprColonExpr and x.len == 2: + var idx = semConstExpr(c, x[0], expectedIndexType) + if not isOrdinalType(idx.typ): + localError(c.config, idx.info, "expected ordinal value for array " & + "index, got '$1'" % renderTree(idx)) + else: + firstIndex = getOrdValue(idx) + lastIndex = firstIndex + indexType = idx.typ + lastValidIndex = lastOrd(c.config, indexType) + x = x[1] + + let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType) + var typ: PType + if constructType: + typ = yy.typ + if expectedElementType == nil: + expectedElementType = typ + else: + typ = expectedElementType + result.add yy + #var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal}) + for i in 1..<n.len: + if lastIndex == lastValidIndex: + let validIndex = makeRangeType(c, toInt64(firstIndex), toInt64(lastValidIndex), n.info, + indexType) + localError(c.config, n.info, "size of array exceeds range of index " & + "type '$1' by $2 elements" % [typeToString(validIndex), $(n.len-i)]) + + x = n[i] + if x.kind == nkExprColonExpr and x.len == 2: + var idx = semConstExpr(c, x[0], indexType) idx = fitNode(c, indexType, idx, x.info) if lastIndex+1 != getOrdValue(idx): localError(c.config, x.info, "invalid order in array constructor") - x = x.sons[1] + x = x[1] - let xx = semExprWithType(c, x, flags*{efAllowDestructor}) + let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType) result.add xx - typ = commonType(typ, xx.typ) - #n.sons[i] = semExprWithType(c, x, flags*{efAllowDestructor}) - #addSon(result, fitNode(c, typ, n.sons[i])) + if constructType: + typ = commonType(c, typ, xx.typ) + #n[i] = semExprWithType(c, x, {}) + #result.add fitNode(c, typ, n[i]) inc(lastIndex) - addSonSkipIntLit(result.typ, typ) - for i in 0 ..< result.len: - result.sons[i] = fitNode(c, typ, result.sons[i], result.sons[i].info) - result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info) + 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.sons[i] + 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.sons[1].typ, abstractVar).kind in + if skipTypes(it[1].typ, abstractVar).kind in {tyNil, tyTuple, tySet} or it[1].isArrayConstr: - var s = skipTypes(it.typ, abstractVar) - if s.kind != tyExpr: - changeType(c, it.sons[1], s, check=true) - n.sons[i] = it.sons[1] - -proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = - result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) + var s = skipTypes(it.typ, abstractVar + tyUserTypeClasses) + if s.kind != tyUntyped: + changeType(c, it[1], s, check=true) + n[i] = it[1] + +proc isAssignable(c: PContext, n: PNode): TAssignableResult = + result = parampatterns.isAssignable(c.p.owner, n) + +proc isUnresolvedSym(s: PSym): bool = + result = s.kind == skGenericParam + if not result and s.typ != nil: + result = tfInferrableStatic in s.typ.flags or + (s.kind == skParam and (s.typ.isMetaType or sfTemplateParam in s.flags)) or + (s.kind == skType and + s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) + +proc hasUnresolvedArgs(c: PContext, n: PNode): bool = + # Checks whether an expression depends on generic parameters that + # don't have bound values yet. E.g. this could happen in situations + # such as: + # type Slot[T] = array[T.size, byte] + # proc foo[T](x: default(T)) + # + # Both static parameter and type parameters can be unresolved. + case n.kind + of nkSym: + return isUnresolvedSym(n.sym) + of nkIdent, nkAccQuoted: + let ident = considerQuotedIdent(c, n) + var amb = false + let sym = searchInScopes(c, ident, amb) + if sym != nil: + return isUnresolvedSym(sym) + else: + return false + else: + for i in 0..<n.safeLen: + if hasUnresolvedArgs(c, n[i]): return true + return false -proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = - if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or +proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode = + if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or sfCompileToCpp in c.module.flags): checkSonsLen(n, 1, c.config) - result = n.sons[0] + result = n[0] else: result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)) - addSon(result, n) - if isAssignable(c, n) notin {arLValue, arLocalLValue}: - localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n)) + result.add n + let aa = isAssignable(c, n) + let sym = getRoot(n) + if aa notin {arLValue, arLocalLValue}: + if aa == arDiscriminant and c.inUncheckedAssignSection > 0: + discard "allow access within a cast(unsafeAssign) section" + elif strictDefs in c.features and aa == arAddressableConst and + sym != nil and sym.kind == skLet and isOutParam: + discard "allow let varaibles to be passed to out parameters" + else: + localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n)) -proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = +proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode = result = n case n.kind of nkSym: @@ -502,44 +892,59 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = if n.sym.typ != nil and skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: incl(n.sym.flags, sfAddrTaken) - result = newHiddenAddrTaken(c, n) + result = newHiddenAddrTaken(c, n, isOutParam) of nkDotExpr: checkSonsLen(n, 2, c.config) - if n.sons[1].kind != nkSym: + if n[1].kind != nkSym: internalError(c.config, n.info, "analyseIfAddressTaken") return - if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: - incl(n.sons[1].sym.flags, sfAddrTaken) - result = newHiddenAddrTaken(c, n) + if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: + incl(n[1].sym.flags, sfAddrTaken) + result = newHiddenAddrTaken(c, n, isOutParam) of nkBracketExpr: checkMinSonsLen(n, 1, c.config) - if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: - if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken) - result = newHiddenAddrTaken(c, n) + if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: + if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken) + result = newHiddenAddrTaken(c, n, isOutParam) else: - result = newHiddenAddrTaken(c, n) + result = newHiddenAddrTaken(c, n, isOutParam) -proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = +proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) = checkMinSonsLen(n, 1, c.config) + if n[0].typ == nil: + # n[0] might be erroring node in nimsuggest + return const FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, - mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy} - + mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, + mWasMoved} + + template checkIfConverterCalled(c: PContext, n: PNode) = + ## Checks if there is a converter call which wouldn't be checked otherwise + # Call can sometimes be wrapped in a deref + let node = if n.kind == nkHiddenDeref: n[0] else: n + if node.kind == nkHiddenCallConv: + analyseIfAddressTakenInCall(c, node, true) # get the real type of the callee # it may be a proc var with a generic alias type, so we skip over them - var t = n.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}) - - if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams: + var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}) + if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams: # BUGFIX: check for L-Value still needs to be done for the arguments! # note sometimes this is eval'ed twice so we check for nkHiddenAddr here: - for i in countup(1, sonsLen(n) - 1): - if i < sonsLen(t) and t.sons[i] != nil and - skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: + for i in 1..<n.len: + if i < t.len and t[i] != nil and + skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}: let it = n[i] - if isAssignable(c, it) notin {arLValue, arLocalLValue}: + let aa = isAssignable(c, it) + if aa notin {arLValue, arLocalLValue}: if it.kind != nkHiddenAddr: - localError(c.config, it.info, errVarForOutParamNeededX % $it) + if aa == arDiscriminant and c.inUncheckedAssignSection > 0: + discard "allow access within a cast(unsafeAssign) section" + else: + localError(c.config, it.info, errVarForOutParamNeededX % $it) + # Make sure to still check arguments for converters + c.checkIfConverterCalled(n[i]) # bug #5113: disallow newSeq(result) where result is a 'var T': if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}: var arg = n[1] #.skipAddr @@ -549,22 +954,22 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments})) return - for i in countup(1, sonsLen(n) - 1): - if n.sons[i].kind == nkHiddenCallConv: - # we need to recurse explicitly here as converters can create nested - # calls and then they wouldn't be analysed otherwise - analyseIfAddressTakenInCall(c, n.sons[i]) - if i < sonsLen(t) and - skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: - if n.sons[i].kind != nkHiddenAddr: - n.sons[i] = analyseIfAddressTaken(c, n.sons[i]) + for i in 1..<n.len: + let n = if n.kind == nkHiddenDeref: n[0] else: n + c.checkIfConverterCalled(n[i]) + if i < t.len and + skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}: + # Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet. + # So we need to make sure we are checking them still when in a converter call + if n[i].kind != nkHiddenAddr or isConverter: + n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc}))) include semmagic proc evalAtCompileTime(c: PContext, n: PNode): PNode = result = n - if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return - var callee = n.sons[0].sym + 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 @@ -572,17 +977,17 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = # constant folding that is necessary for correctness of semantic pass: if callee.magic != mNone and callee.magic in ctfeWhitelist and n.typ != nil: var call = newNodeIT(nkCall, n.info, n.typ) - call.add(n.sons[0]) + call.add(n[0]) var allConst = true - for i in 1 ..< n.len: - var a = getConstExpr(c.module, n.sons[i], c.graph) + for i in 1..<n.len: + var a = getConstExpr(c.module, n[i], c.idgen, c.graph) if a == nil: allConst = false - a = n.sons[i] - if a.kind == nkHiddenStdConv: a = a.sons[1] + a = n[i] + if a.kind == nkHiddenStdConv: a = a[1] call.add(a) if allConst: - result = semfold.getConstExpr(c.module, call, c.graph) + result = semfold.getConstExpr(c.module, call, c.idgen, c.graph) if result.isNil: result = n else: return result @@ -592,49 +997,62 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = # done until we have a more robust infrastructure for # implicit statics. if n.len > 1: - for i in 1 ..< n.len: + for i in 1..<n.len: # see bug #2113, it's possible that n[i].typ for errornous code: if n[i].typ.isNil or n[i].typ.kind != tyStatic or tfUnresolved notin n[i].typ.flags: break maybeLabelAsStatic - n.typ = newTypeWithSons(c, tyStatic, @[n.typ]) + n.typ = newTypeS(tyStatic, c, n.typ) n.typ.flags.incl tfUnresolved # optimization pass: not necessary for correctness of the semantic pass - if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and + if callee.kind == skConst or + {sfNoSideEffect, sfCompileTime} * callee.flags != {} and {sfForward, sfImportc} * callee.flags == {} and n.typ != nil: - if sfCompileTime notin callee.flags and - optImplicitStatic notin c.config.options: return + + 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} or callee.isGenericRoutine: + + if callee.kind notin {skProc, skFunc, skConverter, skConst} or + callee.isGenericRoutineStrict: return - if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return + if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return var call = newNodeIT(nkCall, n.info, n.typ) - call.add(n.sons[0]) - for i in 1 ..< n.len: - let a = getConstExpr(c.module, n.sons[i], c.graph) + 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 sfCompileTime in callee.flags: - result = evalStaticExpr(c.module, c.graph, call, c.p.owner) - if result.isNil: - localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call)) - else: result = fixupTypeAfterEval(c, result, n) + 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 = evalConstExpr(c.module, c.graph, call) - if result.isNil: result = n - else: result = fixupTypeAfterEval(c, result, n) + result = n #if result != n: # echo "SUCCESS evaluated at compile time: ", call.renderTree -proc semStaticExpr(c: PContext, n: PNode): PNode = - let a = semExpr(c, n.sons[0]) +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.graph, a, c.p.owner) + 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 @@ -642,105 +1060,146 @@ proc semStaticExpr(c: PContext, n: PNode): PNode = result = fixupTypeAfterEval(c, result, a) proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, - flags: TExprFlags): PNode = - if flags*{efInTypeof, efWantIterator} != {}: + 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 ``type(countup(1,3))``, see ``tests/ttoseq``. + # for ``typeof(countup(1,3))``, see ``tests/ttoseq``. result = semOverloadedCall(c, n, nOrig, - {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags) + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags, expectedType) else: result = semOverloadedCall(c, n, nOrig, - {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags) + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags, expectedType) if result != nil: - if result.sons[0].kind != nkSym: - internalError(c.config, "semOverloadedCallAnalyseEffects") + if result[0].kind != nkSym: + if not (c.inGenericContext > 0): # see generic context check in semOverloadedCall + internalError(c.config, "semOverloadedCallAnalyseEffects") return - let callee = result.sons[0].sym + let callee = result[0].sym case callee.kind of skMacro, skTemplate: discard else: - if callee.kind == skIterator and callee.id == c.p.owner.id: - localError(c.config, n.info, errRecursiveDependencyX % callee.name.s) + 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.sons[0] = newSymNode(errorSym(c, n)) - -proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode + 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 = - initCandidate(c, result, t) + result = initCandidate(c, t) matches(c, n, nOrig, result) - if result.state != csMatch: - # try to deref the first argument: - if implicitDeref in c.features and canDeref(n): - n.sons[1] = n.sons[1].tryDeref - initCandidate(c, result, t) - matches(c, n, nOrig, result) - -proc bracketedMacro(n: PNode): PSym = - if n.len >= 1 and n[0].kind == nkSym: - result = n[0].sym - if result.kind notin {skMacro, skTemplate}: - result = nil -proc setGenericParams(c: PContext, n: PNode) = - for i in 1 ..< n.len: - n[i].typ = semTypeNode(c, n[i], nil) +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 -proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = result = n - let callee = result.sons[0].sym + + 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) - of skTemplate: result = semTemplateExpr(c, result, callee, flags) + of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType) + of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType) else: - semFinishOperands(c, result) + 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) - if result.typ != nil: liftTypeBoundOps(c, result.typ, n.info) + 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: + 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): PNode = +proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = nil checkMinSonsLen(n, 1, c.config) - var prc = n.sons[0] - if n.sons[0].kind == nkDotExpr: - checkSonsLen(n.sons[0], 2, c.config) - let n0 = semFieldAccess(c, n.sons[0]) + 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.kind = nkCall + result.transitionSonsKind(nkCall) result.flags.incl nfExplicitCall - for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i]) - return semExpr(c, result, flags) + 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.sons[0] = n0 + n[0] = n0 else: - n.sons[0] = semExpr(c, n.sons[0], {efInCall}) - let t = n.sons[0].typ + n[0] = semExpr(c, n[0], {efInCall, efAllowSymChoice}) + let t = n[0].typ if t != nil and t.kind in {tyVar, tyLent}: - n.sons[0] = newDeref(n.sons[0]) - elif n.sons[0].kind == nkBracketExpr: - let s = bracketedMacro(n.sons[0]) - if s != nil: - setGenericParams(c, n[0]) - return semDirectOp(c, n, flags) + 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) - var t: PType = nil - if n.sons[0].typ != nil: - t = skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}) if t != nil and t.kind == tyProc: # This is a proc variable, apply normal overload resolution let m = resolveIndirectCall(c, n, nOrig, t) @@ -752,34 +1211,41 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: var hasErrorType = false var msg = "type mismatch: got <" - for i in countup(1, sonsLen(n) - 1): - if i > 1: add(msg, ", ") - let nt = n.sons[i].typ - add(msg, typeToString(nt)) + 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: - add(msg, ">\nbut expected one of: \n" & - typeToString(n.sons[0].typ)) + 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) - result = nil else: result = m.call instGenericConvertersSons(c, result, m) - elif t != nil and t.kind == tyTypeDesc: - if n.len == 1: return semObjConstr(c, n, flags) - return semConv(c, n) + else: - result = overloadedCallOpr(c, n) + 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: + 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.sons[0] = prc - nOrig.sons[0] = prc + n[0] = prc + nOrig[0] = prc n.flags.incl nfExprCall result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) if result == nil: return errorNode(c, n) @@ -788,48 +1254,38 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # See bug #904 of how to trigger it: return result #result = afterCallActions(c, result, nOrig, flags) - if result.sons[0].kind == nkSym: - result = afterCallActions(c, result, nOrig, flags) + 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): PNode = +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) - if result != nil: result = afterCallActions(c, result, nOrig, flags) + 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) - var e = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "echo")) + let e = systemModuleSym(c.graph, getIdent(c.cache, "echo")) if e != nil: - add(result, newSymNode(e)) + result.add(newSymNode(e)) else: - localError(c.config, n.info, "system needs: echo") - add(result, errorNode(c, n)) - add(result, n) + 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}) - # make an 'if' expression an 'if' statement again for backwards - # compatibility (.discardable was a bad idea!); bug #6980 - var isStmt = false - if result.kind == nkIfExpr: - isStmt = true - for condActionPair in result: - let action = condActionPair.lastSon - if not implicitlyDiscardable(action) and not - endsInNoReturn(action): - isStmt = false - if isStmt: - result.kind = nkIfStmt - result.typ = nil - discardCheck(c, result) + discardCheck(c, result, {}) + if isPush: popInfoContext(c.config) proc isTypeExpr(n: PNode): bool = case n.kind @@ -849,50 +1305,50 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, result = nil case r.kind of nkRecList: - for i in countup(0, sonsLen(r) - 1): - result = lookupInRecordAndBuildCheck(c, n, r.sons[i], field, check) + 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.sons[0].kind != nkSym): illFormedAst(r, c.config) - result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check) + 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.sons[0].typ) + let setType = createSetType(c, r[0].typ) var s = newNodeIT(nkCurly, r.info, setType) - for i in countup(1, sonsLen(r) - 1): - var it = r.sons[i] + 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..sonsLen(it)-2: addSon(s, copyTree(it.sons[j])) + for j in 0..<it.len-1: s.add copyTree(it[j]) else: if check == nil: check = newNodeI(nkCheckedFieldExpr, n.info) - addSon(check, c.graph.emptyNode) # make space for access node + check.add c.graph.emptyNode # make space for access node s = newNodeIT(nkCurly, n.info, setType) - for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j])) + for j in 0..<it.len - 1: s.add copyTree(it[j]) var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) - addSon(inExpr, newSymNode(c.graph.opContains, n.info)) - addSon(inExpr, s) - addSon(inExpr, copyTree(r.sons[0])) - addSon(check, inExpr) - #addSon(check, semExpr(c, inExpr)) + 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) - addSon(check, c.graph.emptyNode) # make space for access node + check.add c.graph.emptyNode # make space for access node var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) - addSon(inExpr, newSymNode(c.graph.opContains, n.info)) - addSon(inExpr, s) - addSon(inExpr, copyTree(r.sons[0])) + 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)) - addSon(notExpr, newSymNode(c.graph.opNot, n.info)) - addSon(notExpr, inExpr) - addSon(check, notExpr) + 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: @@ -901,7 +1357,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, const tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} - tyDotOpTransparent = {tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink} + tyDotOpTransparent = {tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink} proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = @@ -919,7 +1375,7 @@ proc readTypeParameter(c: PContext, typ: PType, # This seems semantically correct and then we'll be able # to return the section symbol directly here let foundType = makeTypeDesc(c, def[2].typ) - return newSymNode(copySym(def[0].sym).linkTo(foundType), info) + return newSymNode(copySym(def[0].sym, c.idgen).linkTo(foundType), info) of nkConstSection: for def in statement: @@ -930,13 +1386,13 @@ proc readTypeParameter(c: PContext, typ: PType, discard if typ.kind != tyUserTypeClass: - let ty = if typ.kind == tyCompositeTypeClass: typ.sons[1].skipGenericAlias + let ty = if typ.kind == tyCompositeTypeClass: typ.firstGenericParam.skipGenericAlias else: typ.skipGenericAlias - let tbody = ty.sons[0] - for s in countup(0, tbody.len-2): - let tParam = tbody.sons[s] + 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.sons[s + 1] + let rawTyp = ty[s + 1] if rawTyp.kind == tyStatic: if rawTyp.n != nil: return rawTyp.n @@ -944,18 +1400,22 @@ proc readTypeParameter(c: PContext, typ: PType, return c.graph.emptyNode else: let foundTyp = makeTypeDesc(c, rawTyp) - return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) + 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: - markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) - case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind - of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, + 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) @@ -970,59 +1430,55 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = # It is clear that ``[]`` means two totally different things. Thus, we # copy `x`'s AST into each context, so that the type fixup phase can # deal with two different ``[]``. - if s.ast.len == 0: result = inlineConst(c, n, s) + 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: - if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or - (n.kind notin nkCallKinds and s.requiredParams > 0): - markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) - result = symChoice(c, n, s, scClosed) - else: - result = semMacroExpr(c, n, n, s, flags) - of skTemplate: - if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or - (n.kind notin nkCallKinds and s.requiredParams > 0) or - sfCustomPragma in sym.flags: - markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + 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: - result = semTemplateExpr(c, n, s, flags) + 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.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + 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: - if c.p.wasForwarded: - # gensym'ed parameters that nevertheless have been forward declared - # need a special fixup: - let realParam = c.p.owner.typ.n[s.position+1] - internalAssert c.config, realParam.kind == nkSym and realParam.sym.kind == skParam - return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info) - elif c.p.owner.kind == skMacro: - # gensym'ed macro parameters need a similar hack (see bug #1944): - var u = searchInScopes(c, s.name) - internalAssert c.config, u != nil and u.kind == skParam and u.owner == s.owner - return newSymNode(u, n.info) + # 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") - markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + 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: - styleCheckUse(n.info, s) + onUse(n.info, s) if s.typ.kind == tyStatic: result = newSymNode(s, n.info) result.typ = s.typ @@ -1032,56 +1488,101 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = n.typ = s.typ return n of skType: - markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) - if s.typ.kind == tyStatic and s.typ.n != nil: + if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess + markUsed(c, n.info, s) + onUse(n.info, 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: - var p = c.p - while p != nil and p.selfSym == nil: - p = p.next - if p != nil and p.selfSym != nil: - var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, - tyAlias, tySink}) - while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) - var check: PNode = nil - if ty.kind == tyObject: - while true: - check = nil - let f = lookupInRecordAndBuildCheck(c, n, ty.n, s.name, check) - if f != nil and fieldVisible(c, f): - # is the access to a public field or in the same module or in a friend? - doAssert f == s - markUsed(c.config, n.info, f, c.graph.usageSym) - styleCheckUse(n.info, f) - result = newNodeIT(nkDotExpr, n.info, f.typ) - result.add makeDeref(newSymNode(p.selfSym)) - result.add newSymNode(f) # we now have the correct field - if check != nil: - check.sons[0] = result - check.typ = result.typ - result = check - return result - if ty.sons[0] == nil: break - ty = skipTypes(ty.sons[0], skipPtrs) # old code, not sure if it's live code: - markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + markUsed(c, n.info, s) + onUse(n.info, s) result = newSymNode(s, n.info) - else: - markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + 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 builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = +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.sons[1]): return + #if isSymChoice(n[1]): return when defined(nimsuggest): if c.config.cmd == cmdIdeTools: suggestExpr(c, n) @@ -1093,40 +1594,19 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = symChoice(c, n, s, scClosed) if result.kind == nkSym: result = semSym(c, n, s, flags) else: - markUsed(c.config, n.sons[1].info, s, c.graph.usageSym) + markUsed(c, n[1].info, s) result = semSym(c, n, s, flags) - styleCheckUse(n.sons[1].info, s) + onUse(n[1].info, s) return - n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) - #restoreOldStyleType(n.sons[0]) - var i = considerQuotedIdent(c, n.sons[1], n) - var ty = n.sons[0].typ + # 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 - template tryReadingGenericParam(t: PType) = - case t.kind - of tyTypeParamsHolders: - result = readTypeParameter(c, t, i, n.info) - if result == c.graph.emptyNode: - result = n - n.typ = makeTypeFromExpr(c, n.copyTree) - return - of tyUserTypeClasses: - if t.isResolvedUserTypeClass: - return readTypeParameter(c, t, i, n.info) - else: - n.typ = makeTypeFromExpr(c, copyTree(n)) - return n - of tyGenericParam, tyAnything: - n.typ = makeTypeFromExpr(c, copyTree(n)) - return n - else: - discard - - var argIsType = false - if ty.kind == tyTypeDesc: if ty.base.kind == tyNone: # This is a still unresolved typedesc parameter. @@ -1135,149 +1615,183 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # 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: - ty = ty.base - argIsType = true - else: - argIsType = isTypeExpr(n.sons[0]) - - if argIsType: - ty = ty.skipTypes(tyDotOpTransparent) - case ty.kind - of tyEnum: - # look up if the identifier belongs to the enum: - while ty != nil: - f = getSymFromList(ty.n, i) - if f != nil: break - ty = ty.sons[0] # enum inheritance - if f != nil: - result = newSymNode(f) - result.info = n.info - result.typ = ty - markUsed(c.config, n.info, f, c.graph.usageSym) - styleCheckUse(n.info, f) - return - of tyObject, tyTuple: - if ty.n != nil and ty.n.kind == nkRecList: - let field = lookupInRecord(ty.n, i) - if field != nil: - n.typ = makeTypeDesc(c, field.typ) - return n - else: - tryReadingGenericParam(ty) - return - # XXX: This is probably not relevant any more - # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": - ty = n.sons[0].typ + 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.lastSon - ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) - while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) + 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.sons[0] == nil: break - ty = skipTypes(ty.sons[0], skipPtrs) + if ty[0] == nil: break + ty = skipTypes(ty[0], skipPtrs) if f != nil: - if fieldVisible(c, f): + 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.config, n.sons[1].info, f, c.graph.usageSym) - styleCheckUse(n.sons[1].info, f) - n.sons[0] = makeDeref(n.sons[0]) - n.sons[1] = newSymNode(f) # we now have the correct field + 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.sons[0] = n + 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.config, n.sons[1].info, f, c.graph.usageSym) - styleCheckUse(n.sons[1].info, f) - n.sons[0] = makeDeref(n.sons[0]) - n.sons[1] = newSymNode(f) + 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.sons[0].typ.skipTypes(tyDotOpTransparent) - tryReadingGenericParam(t) + let t = n[0].typ.skipTypes(tyDotOpTransparent) + result = tryReadingGenericParam(c, n, i, t) + flags.incl efCannotBeDotCall proc dotTransformation(c: PContext, n: PNode): PNode = - if isSymChoice(n.sons[1]): + 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) - addSon(result, n.sons[1]) - addSon(result, copyTree(n[0])) + result.add n[1] + result.add copyTree(n[0]) else: - var i = considerQuotedIdent(c, n.sons[1], n) + var i = considerQuotedIdent(c, n[1], n) result = newNodeI(nkDotCall, n.info) result.flags.incl nfDotField - addSon(result, newIdentNode(i, n[1].info)) - addSon(result, copyTree(n[0])) + result.add newIdentNode(i, n[1].info) + result.add copyTree(n[0]) proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # this is difficult, because the '.' is used in many different contexts # in Nim. We first allow types in the semantic checking. - result = builtinFieldAccess(c, n, flags) - if result == nil: + var f = flags - {efIsDotCall} + result = builtinFieldAccess(c, n, f) + if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and + efIsDotCall in flags and callOperator notin c.features and + efCannotBeDotCall notin f): result = dotTransformation(c, n) proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = result = newNodeI(nkCall, n.info) result.add(newIdentNode(ident, n.info)) - for i in 0 .. n.len-1: result.add(n[i]) + for s in n: result.add s -proc semDeref(c: PContext, n: PNode): PNode = +proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode = checkSonsLen(n, 1, c.config) - n.sons[0] = semExprWithType(c, n.sons[0]) + 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.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink}) + var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned}) case t.kind - of tyRef, tyPtr: n.typ = t.lastSon + of tyRef, tyPtr: n.typ = t.elementType + of tyMetaTypes, tyFromExpr: + n.typ = makeTypeFromExpr(c, n.copyTree) else: result = nil - #GlobalError(n.sons[0].info, errCircumNeedsPointer) + #GlobalError(n[0].info, errCircumNeedsPointer) + +proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode = + ## Instantiates generic if not lacking implicit generics, + ## otherwise returns n. + let + neededGenParams = s.ast[genericParamsPos].len + heldGenParams = n.len - 1 + var implicitParams = 0 + for x in s.ast[genericParamsPos]: + if tfImplicitTypeParam in x.typ.flags: + inc implicitParams + if heldGenParams != neededGenParams and implicitParams + heldGenParams == neededGenParams: + # This is an implicit + explicit generic procedure without all args passed, + # kicking back the sem'd symbol fixes #17212 + # Uncertain the hackiness of this solution. + result = n + else: + result = explicitGenericInstantiation(c, n, s) + if result == n: + n[0] = copyTree(result[0]) proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if not a built-in subscript operator; also called for the ## checking of assignments - if sonsLen(n) == 1: - let x = semDeref(c, n) + 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) - # make sure we don't evaluate generic macros/templates - n.sons[0] = semExprWithType(c, n.sons[0], - {efNoEvaluateGeneric}) - let arr = skipTypes(n.sons[0].typ, {tyGenericInst, + # 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) + else: + arr = arr.base + case arr.kind - of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, - tyCString: + of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCstring, + tyUncheckedArray: if n.len != 2: return nil - n.sons[0] = makeDeref(n.sons[0]) - for i in countup(1, sonsLen(n) - 1): - n.sons[i] = semExprWithType(c, n.sons[i], + n[0] = makeDeref(n[0]) + for i in 1..<n.len: + n[i] = semExprWithType(c, n[i], flags*{efInTypeof, efDetermineType}) - var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(c.graph, n.info, tyInt) - var arg = indexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1]) - if arg != nil: - n.sons[1] = arg + # 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) - #GlobalError(n.info, errIndexTypesDoNotMatch) of tyTypeDesc: # The result so far is a tyTypeDesc bound # a tyGenericBody. The line below will substitute @@ -1287,27 +1801,30 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = #result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) of tyTuple: if n.len != 2: return nil - n.sons[0] = makeDeref(n.sons[0]) + n[0] = makeDeref(n[0]) # [] operator for tuples requires constant expression: - n.sons[1] = semConstExpr(c, n.sons[1]) - if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in + n[1] = semConstExpr(c, n[1]) + if skipTypes(n[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in {tyInt..tyInt64}: - var idx = getOrdValue(n.sons[1]) - if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)] - else: localError(c.config, n.info, "invalid index value for tuple subscript") + 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.sons[0].kind == nkSym: n.sons[0].sym - elif n[0].kind in nkSymChoices: n.sons[0][0].sym + 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.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) - result = explicitGenericInstantiation(c, n, s) + n[0] = semSymGenericInstantiation(c, n[0], s) + result = maybeInstantiateGeneric(c, n, s) of skMacro, skTemplate: if efInCall in flags: # We are processing macroOrTmpl[] in macroOrTmpl[](...) call. @@ -1317,7 +1834,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = else: # We are processing macroOrTmpl[] not in call. Transform it to the # macro or template call with generic arguments here. - n.kind = nkCall + n.transitionSonsKind(nkCall) case s.kind of skMacro: result = semMacroExpr(c, n, n, s, flags) of skTemplate: result = semTemplateExpr(c, n, s, flags) @@ -1327,11 +1844,11 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = else: discard -proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = +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, "[]"))) + 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) @@ -1340,10 +1857,9 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # nodes? let aOrig = nOrig[0] - result = newNode(nkCall, n.info, sons = @[setterId, a[0], - semExprWithType(c, n[1])]) + result = newTreeI(nkCall, n.info, setterId, a[0], n[1]) result.flags.incl nfDotSetter - let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]]) + let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1]) result = semOverloadedCallAnalyseEffects(c, result, orig, {}) if result != nil: @@ -1356,48 +1872,143 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = # 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/var_t_return.html" % [ - root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl]) + 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/var_t_return.html" % [ - root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl]) + 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 nkHiddenDeref, nkDerefExpr: return n.sons[0] + of nkDerefExpr: return n[0] of nkBracketExpr: - if len(n) == 1: return n.sons[0] + 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 == arLocalLValue: + if valid in {arAddressableConst, arLentValue} and isLent: + discard "ok" + elif valid == arLocalLValue: localError(c.config, n.info, errXStackEscape % renderTree(n, {renderNoComments})) - elif not isLent: + else: localError(c.config, n.info, errExprHasNoAddress) - result = newNodeIT(nkHiddenAddr, n.info, makePtrType(c, n.typ)) + 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.sons[0] - if x.typ.kind in {tyVar, tyLent} and x.kind == nkSym and x.sym.kind == skResult: - n.sons[0] = x # 'result[]' --> 'result' - n.sons[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 + 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.sons[0] + var a = n[0] case a.kind of nkDotExpr: # r.f = x # --> `f=` (r, x) let nOrig = n.copyTree - a = builtinFieldAccess(c, a, {efLValue}) + var flags = {efLValue} + a = builtinFieldAccess(c, a, flags) if a == nil: a = propertyWriteAccess(c, n, nOrig, n[0]) if a != nil: return a @@ -1405,102 +2016,112 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # possible: a = dotTransformation(c, n[0]) if a.kind == nkDotCall: - a.kind = nkCall + 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.sons[0], getIdent(c.cache, "[]=")) - add(result, n[1]) + result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]=")) + result.add(n[1]) if mode == noOverloadedSubscript: - bracketNotFoundError(c, result) - return n + 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.sons[0], getIdent(c.cache, "{}=")) - add(result, n[1]) + result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "{}=")) + result.add(n[1]) return semExprNoType(c, result) of nkPar, nkTupleConstr: - if a.len >= 2: + 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, lowerTupleUnpackingForAsgn(c.graph, n, c.p.owner)) + return semStmt(c, makeTupleAssignments(c, n), {}) else: a = semExprWithType(c, a, {efLValue}) else: a = semExprWithType(c, a, {efLValue}) - n.sons[0] = a + 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 - if (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind != tyVar and - isAssignable(c, a) == arNone) or - skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}: + 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.sons[0] - lhsIsResult = lhs.kind == nkSym and lhs.sym.kind == skResult - var - rhs = semExprWithType(c, n.sons[1], - if lhsIsResult: {efAllowDestructor} else: {}) - if lhsIsResult: + 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.lastSon + 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.sons[0] = rhsTyp + c.p.owner.typ.setReturnType rhsTyp else: - typeMismatch(c.config, n.info, lhs.typ, rhsTyp) + typeMismatch(c.config, n.info, lhs.typ, rhsTyp, rhs) + borrowCheck(c, n, lhs, rhs) - n.sons[1] = fitNode(c, le, rhs, n.info) - if destructor notin c.features: - if tfHasAsgn in lhs.typ.flags and not lhsIsResult and - mode != noOverloadedAsgn: - return overloadedAsgn(c, lhs, n.sons[1]) - else: - liftTypeBoundOps(c, lhs.typ, lhs.info) + n[1] = fitNode(c, le, rhs, goodLineInfo(n[1])) + when false: liftTypeBoundOps(c, lhs.typ, lhs.info) fixAbstractType(c, n) - asgnToResultVar(c, n, n.sons[0], n.sons[1]) + 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 ( - c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure): - if n.sons[0].kind != nkEmpty: - # transform ``return expr`` to ``result = expr; return`` - if c.p.resultSym != nil: - var a = newNodeI(nkAsgn, n.sons[0].info) - addSon(a, newSymNode(c.p.resultSym)) - addSon(a, n.sons[0]) - n.sons[0] = semAsgn(c, a) - # optimize away ``result = result``: - if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym: - n.sons[0] = c.graph.emptyNode + 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): PNode = +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) + result = semExpr(c, n, expectedType = expectedType) if c.p.resultSym != nil and not isEmptyType(result.typ): if result.kind == nkNilLit: # or ImplicitlyDiscardable(result): @@ -1514,110 +2135,137 @@ proc semProcBody(c: PContext, n: PNode): PNode = fixNilType(c, result) else: var a = newNodeI(nkAsgn, n.info, 2) - a.sons[0] = newSymNode(c.p.resultSym) - a.sons[1] = result + a[0] = newSymNode(c.p.resultSym) + a[1] = result result = semAsgn(c, a) else: - discardCheck(c, result) + discardCheck(c, result, {}) if c.p.owner.kind notin {skMacro, skTemplate} and c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: if isEmptyType(result.typ): # we inferred a 'void' return type: c.p.resultSym.typ = errorType(c) - c.p.owner.typ.sons[0] = nil + c.p.owner.typ.setReturnType nil else: - localError(c.config, c.p.resultSym.info, errCannotInferReturnType) - + 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: - if t.kind == tyVar: t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 - if n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: - n.sons[0] = n.sons[0].sons[1] - n.sons[0] = takeImplicitAddr(c, n.sons[0], t.kind == 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.sonsLen: - var e = skipTypes(t.sons[i], {tyGenericInst, tyAlias, tySink}) + for i in 0..<t.len: + let e = skipTypes(t[i], {tyGenericInst, tyAlias, tySink}) if e.kind in {tyVar, tyLent}: - if e.kind == tyVar: e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 - if n.sons[0].kind in {nkPar, nkTupleConstr}: - n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i], e.kind == tyLent) - elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and - n.sons[0].sons[1].kind in {nkPar, nkTupleConstr}: - var a = n.sons[0].sons[1] - a.sons[i] = takeImplicitAddr(c, a.sons[i], false) + 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.sons[0].info, errXExpected, "tuple constructor") - else: discard + 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.sons[0].kind != nkEmpty: - n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: + elif n[0].kind != nkEmpty: var iterType = c.p.owner.typ - let restype = iterType.sons[0] + let restype = iterType[0] + n[0] = semExprWithType(c, n[0], {}, restype) # check for type compatibility: if restype != nil: - if restype.kind != tyExpr: - n.sons[0] = fitNode(c, restype, n.sons[0], n.info) - if n.sons[0].typ == nil: internalError(c.config, n.info, "semYield") + if n[0].typ == nil: internalError(c.config, n.info, "semYield") if resultTypeIsInferrable(restype): - let inferred = n.sons[0].typ - iterType.sons[0] = inferred + 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.sons[0] != nil: + elif c.p.owner.typ.returnType != nil: localError(c.config, n.info, errGenerated, "yield statement must yield a value") -proc lookUpForDefined(c: PContext, i: PIdent, onlyCurrentScope: bool): PSym = - if onlyCurrentScope: - result = localSearchInScope(c, i) +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 = searchInScopes(c, i) # no need for stub loading + 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 lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = +proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = case n.kind - of nkIdent: - result = lookUpForDefined(c, n.ident, onlyCurrentScope) + 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 = lookUpForDefined(c, n.sons[0], onlyCurrentScope) + 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 = strTableGet(m.tab, ident) - of nkAccQuoted: - result = lookUpForDefined(c, considerQuotedIdent(c, n), onlyCurrentScope) + result = someSym(c.graph, m, ident) of nkSym: result = n.sym of nkOpenSymChoice, nkClosedSymChoice: - result = n.sons[0].sym + result = n[0].sym + of nkOpenSym: + result = lookUpForDeclared(c, n[0], onlyCurrentScope) else: localError(c.config, n.info, "identifier expected, but got: " & renderTree(n)) result = nil -proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = +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) - if not onlyCurrentScope and considerQuotedIdent(c, n[0], n).s == "defined": - let d = considerQuotedIdent(c, n[1], n) - result.intVal = ord isDefined(c.config, d.s) - elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil: - result.intVal = 1 + result.intVal = ord lookUpForDeclared(c, n[1], onlyCurrentScope) != nil result.info = n.info result.typ = getSysType(c.graph, n.info, tyBool) @@ -1644,11 +2292,11 @@ proc expectString(c: PContext, n: PNode): string = 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, getCurrOwner(c), info) - result.flags = {sfGenSym} + result = newSym(kind, c.cache.idAnon, c.idgen, getCurrOwner(c), info) proc semExpandToAst(c: PContext, n: PNode): PNode = let macroCall = n[1] @@ -1657,19 +2305,19 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = let expandedSym = expectMacroOrTemplateCall(c, macroCall) if expandedSym.kind == skError: return n - macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) - markUsed(c.config, n.info, expandedSym, c.graph.usageSym) - styleCheckUse(n.info, expandedSym) + macroCall[0] = newSymNode(expandedSym, macroCall.info) + markUsed(c, n.info, expandedSym) + onUse(n.info, expandedSym) if isCallExpr(macroCall): - for i in countup(1, macroCall.len-1): - #if macroCall.sons[0].typ.sons[i].kind != tyExpr: - macroCall.sons[i] = semExprWithType(c, macroCall[i], {}) + 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 + 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: @@ -1681,17 +2329,17 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = elif cands >= 2: localError(c.config, n.info, "ambiguous symbol in 'getAst' context: " & $macroCall) else: - let info = macroCall.sons[0].info - macroCall.sons[0] = newSymNode(cand, info) - markUsed(c.config, info, cand, c.graph.usageSym) - styleCheckUse(info, cand) + let info = macroCall[0].info + macroCall[0] = newSymNode(cand, info) + markUsed(c, info, cand) + onUse(info, cand) # we just perform overloading resolution here: - #n.sons[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro}) + #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.sons[0].sym.magic == mExpandToAst + 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 @@ -1699,8 +2347,8 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, flags: TExprFlags = {}): PNode = - if sonsLen(n) == 2: - n.sons[0] = newSymNode(magicSym, n.info) + if n.len == 2: + n[0] = newSymNode(magicSym, n.info) result = semExpandToAst(c, n) else: result = semDirectOp(c, n, flags) @@ -1714,73 +2362,113 @@ proc processQuotations(c: PContext; n: var PNode, op: string, ids.add n return - if n.kind == nkPrefix: - checkSonsLen(n, 2, c.config) - if n[0].kind == nkIdent: - var examinedOp = n[0].ident.s + template handlePrefixOp(prefixed) = + if prefixed[0].kind == nkIdent: + let examinedOp = prefixed[0].ident.s if examinedOp == op: - returnQuote n[1] + returnQuote prefixed[1] elif examinedOp.startsWith(op): - n.sons[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info) - elif n.kind == nkAccQuoted and op == "``": - returnQuote n[0] + prefixed[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), prefixed.info) - for i in 0 ..< n.safeLen: - processQuotations(c, n.sons[i], op, quotes, ids) + 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 = - internalAssert c.config, n.len == 2 or n.len == 3 + 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](1) + quotes = newSeq[PNode](2) # the quotes will be added to a nkCall statement - # leave some room for the callee symbol - ids = newSeq[PNode]() + # 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 = newAnonSym(c, skTemplate, n.info).newSymNode, + name = dummyTemplateSym.newSymNode, pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode, pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode) if ids.len > 0: - dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info) - dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "typed").newSymNode # return type - ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type - ids.add c.graph.emptyNode # no default value - dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids) - + 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] - result = newNode(nkCall, n.info, @[ - createMagic(c.graph, "getAst", mExpandToAst).newSymNode, - newNode(nkCall, n.info, quotes)]) + # 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 - inc c.compilesContextIdGenerator - c.compilesContextId = c.compilesContextIdGenerator - # do not halt after first error: - c.config.errorMax = high(int) + # 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 = len(c.graph.owners) + let oldOwnerLen = c.graph.owners.len let oldGenerics = c.generics let oldErrorOutputs = c.config.m.errorOutputs if efExplain notin flags: c.config.m.errorOutputs = {} @@ -1795,9 +2483,12 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = var err: string try: result = semExpr(c, n, flags) - if c.config.errorCounter != oldErrorCount: result = nil + if result != nil and efNoSem2Check notin flags: + trackStmt(c, c.module, result, isTopLevel = false) + if c.config.errorCounter != oldErrorCount: + result = nil except ERecoverableError: - discard + result = nil # undo symbol table changes (as far as it's possible): c.compilesContextId = oldCompilesId c.generics = oldGenerics @@ -1812,17 +2503,20 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = 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 sonsLen(n) != 2: return semDirectOp(c, n, flags) + 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 sonsLen(n) == 3: + 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) @@ -1833,9 +2527,9 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semDirectOp(c, n, flags) proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType = - result = newType(tyGenericInvocation, c.module) - addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ) - addSonSkipIntLit(result, t) + 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; @@ -1843,84 +2537,107 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType; let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar") if sym == nil: localError(c.config, info, "system needs: nimCreateFlowVar") - var bindings: TIdTable - initIdTable(bindings) - bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t) + var bindings = initTypeMapping() + bindings.idTablePut(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 = result.flags - {sfCompilerProc, sfExportC, sfImportC} - result.loc.r = nil + result.flags.excl {sfCompilerProc, sfExportc, sfImportc} + result.loc.snippet = "" proc setMs(n: PNode, s: PSym): PNode = result = n - n.sons[0] = newSymNode(s) - n.sons[0].info = n.info + n[0] = newSymNode(s) + n[0].info = n.info -proc extractImports(n: PNode; result: PNode) = - if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}: - result.add copyTree(n) - n.kind = nkEmpty - return - for i in 0..<n.safeLen: extractImports(n[i], result) +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): PNode = +proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode = # this is a hotspot in the compiler! - # DON'T forget to update ast.SpecialSemMagics if you add a magic here! result = n case s.magic # magics that need special treatment of mAddr: + markUsed(c, n.info, s) checkSonsLen(n, 2, c.config) - result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr") + result = semAddr(c, n[1]) of mTypeOf: - checkSonsLen(n, 2, c.config) - result = semTypeOf(c, n.sons[1]) - #of mArrGet: result = semArrGet(c, n, flags) - #of mArrPut: result = semArrPut(c, n, flags) - #of mAsgn: result = semAsgnOpr(c, n) - of mDefined: result = semDefined(c, setMs(n, s), false) - of mDefinedInScope: result = semDefined(c, setMs(n, s), true) - of mCompiles: result = semCompiles(c, setMs(n, s), flags) - #of mLow: result = semLowHigh(c, setMs(n, s), mLow) - #of mHigh: result = semLowHigh(c, setMs(n, s), mHigh) - of mSizeOf: result = semSizeof(c, setMs(n, s)) - of mIs: result = semIs(c, setMs(n, s), flags) - #of mOf: result = semOf(c, setMs(n, s)) - of mShallowCopy: result = semShallowCopy(c, n, flags) - of mExpandToAst: result = semExpandToAst(c, n, s, flags) - of mQuoteAst: result = semQuoteAst(c, n) + 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.sons[bodyPos] + if x.kind == nkDo: x = x[bodyPos] inc c.inParallelStmt - result.sons[1] = semStmt(c, x) + result[1] = semStmt(c, x, {}) dec c.inParallelStmt of mSpawn: - result = setMs(n, s) - for i in 1 ..< n.len: - result.sons[i] = semExpr(c, n.sons[i]) - let typ = result[^1].typ - if not typ.isEmptyType: - if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar: - result.typ = createFlowVar(c, typ, n.info) - else: - result.typ = typ - result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode + markUsed(c, n.info, s) + when defined(leanCompiler): + result = localErrorNode(c, n, "compiler was built without 'spawn' support") else: - result.add c.graph.emptyNode + 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.sons[1] = semExpr(c, n.sons[1]) + 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) @@ -1928,7 +2645,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = if result == nil: result = errorNode(c, n) else: - let callee = result.sons[0].sym + let callee = result[0].sym if callee.magic == mNone: semFinishOperands(c, result) activate(c, result) @@ -1937,29 +2654,50 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = if callee.magic != mNone: result = magicsAfterOverloadResolution(c, result, flags) of mRunnableExamples: - if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: - 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) - extractImports(n.lastSon, imports) - for imp in imports: c.runnableExamples.add imp - c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree n.lastSon) + 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) + 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) # do not open a new scope! + if semCheck: result = semExpr(c, e, flags) # do not open a new scope! else: result = e # Check if the node is "when nimvm" @@ -1969,105 +2707,151 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = # ... var whenNimvm = false var typ = commonTypeBegin - if n.sons.len == 2 and n.sons[0].kind == nkElifBranch and - n.sons[1].kind == nkElse: - let exprNode = n.sons[0].sons[0] + if 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 - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] + 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.sons[1] = semExpr(c, it.sons[1]) - typ = commonType(typ, it.sons[1].typ) + 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: - var e = semConstExpr(c, it.sons[0]) + 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.sons[1]) + setResult(it[1]) + return # we're not in nimvm and we already have a result of nkElse, nkElseExpr: checkSonsLen(it, 1, c.config) - if result == nil or whenNimvm: + if cannotResolve: + discard + elif result == nil or whenNimvm: if semCheck: - it.sons[0] = semExpr(c, it.sons[0]) - typ = commonType(typ, it.sons[0].typ) + 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.sons[0] + 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 - # The ``when`` statement implements the mechanism for platform dependent - # code. Thus we try to ensure here consistent ID allocation after the - # ``when`` statement. - idSynchronizationPoint(200) + if whenNimvm: + result.typ = typ + if n.len == 1: + result.add(newTree(nkElse, newNode(nkStmtList))) -proc semSetConstr(c: PContext, n: PNode): PNode = +proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode = result = newNodeI(nkCurly, n.info) result.typ = newTypeS(tySet, c) - if sonsLen(n) == 0: - rawAddSon(result.typ, newTypeS(tyEmpty, c)) + result.typ.flags.incl tfIsConstructor + var expectedElementType: PType = nil + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind == tySet): + expectedElementType = expected[0] + if n.len == 0: + rawAddSon(result.typ, + if expectedElementType != nil and + typeAllowed(expectedElementType, skLet, c) == nil: + expectedElementType + else: + newTypeS(tyEmpty, c)) else: # only semantic checking for all elements, later type checking: var typ: PType = nil - for i in countup(0, sonsLen(n) - 1): - if isRange(n.sons[i]): - checkSonsLen(n.sons[i], 3, c.config) - n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1]) - n.sons[i].sons[2] = semExprWithType(c, n.sons[i].sons[2]) - if typ == nil: - typ = skipTypes(n.sons[i].sons[1].typ, + for i in 0..<n.len: + let doSetType = typ == nil + if isRange(n[i]): + checkSonsLen(n[i], 3, c.config) + n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType) + n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType) + if doSetType: + typ = skipTypes(n[i][1].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) - n.sons[i].typ = n.sons[i].sons[2].typ # range node needs type too - elif n.sons[i].kind == nkRange: + n[i].typ = n[i][2].typ # range node needs type too + elif n[i].kind == nkRange: # already semchecked - if typ == nil: - typ = skipTypes(n.sons[i].sons[0].typ, + if doSetType: + typ = skipTypes(n[i][0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) else: - n.sons[i] = semExprWithType(c, n.sons[i]) - if typ == nil: - typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) - if not isOrdinalType(typ): - localError(c.config, n.info, errOrdinalTypeExpected) - typ = makeRangeType(c, 0, MaxSetElements-1, n.info) - elif lengthOrd(c.config, typ) > MaxSetElements: - typ = makeRangeType(c, 0, MaxSetElements-1, n.info) - addSonSkipIntLit(result.typ, typ) - for i in countup(0, sonsLen(n) - 1): + n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType) + if doSetType: + typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) + if doSetType: + if not isOrdinalType(typ, allowEnumWithHoles=true): + localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc)) + typ = makeRangeType(c, 0, MaxSetElements-1, n.info) + elif isIntLit(typ): + # set of int literal, use a default range smaller than the max range + typ = makeRangeType(c, 0, DefaultSetElements-1, n.info) + elif lengthOrd(c.config, typ) > MaxSetElements: + message(c.config, n.info, warnAboveMaxSizeSet, "type '" & + typeToString(typ, preferDesc) & "' is too big to be a `set` element, " & + "assuming a range of 0.." & $(MaxSetElements - 1) & + ", explicitly write this range to get rid of warning") + typ = makeRangeType(c, 0, MaxSetElements-1, n.info) + if expectedElementType == nil: + expectedElementType = typ + addSonSkipIntLit(result.typ, typ, c.idgen) + for i in 0..<n.len: var m: PNode - let info = n.sons[i].info - if isRange(n.sons[i]): + let info = n[i].info + if isRange(n[i]): m = newNodeI(nkRange, info) - addSon(m, fitNode(c, typ, n.sons[i].sons[1], info)) - addSon(m, fitNode(c, typ, n.sons[i].sons[2], info)) - elif n.sons[i].kind == nkRange: m = n.sons[i] # already semchecked + 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.sons[i], info) - addSon(result, m) + m = fitNode(c, typ, n[i], info) + result.add m -proc semTableConstr(c: PContext, n: PNode): PNode = +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-1: - var x = n.sons[i] - if x.kind == nkExprColonExpr and sonsLen(x) == 2: - for j in countup(lastKey, i-1): + 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.sons[j]) + pair.add(n[j]) pair.add(x[1]) result.add(pair) @@ -2079,94 +2863,118 @@ proc semTableConstr(c: PContext, n: PNode): PNode = lastKey = i+1 if lastKey != n.len: illFormedAst(n, c.config) - result = semExpr(c, result) + result = semExpr(c, result, expectedType = expectedType) type TParKind = enum paNone, paSingle, paTupleFields, paTuplePositions proc checkPar(c: PContext; n: PNode): TParKind = - var length = sonsLen(n) - if length == 0: + if n.len == 0: result = paTuplePositions # () - elif length == 1: - if n.sons[0].kind == nkExprColonExpr: result = paTupleFields + elif n.len == 1: + if n[0].kind == nkExprColonExpr: result = paTupleFields elif n.kind == nkTupleConstr: result = paTuplePositions else: result = paSingle # (expr) else: - if n.sons[0].kind == nkExprColonExpr: result = paTupleFields + if n[0].kind == nkExprColonExpr: result = paTupleFields else: result = paTuplePositions - for i in countup(0, length - 1): + for i in 0..<n.len: if result == paTupleFields: - if (n.sons[i].kind != nkExprColonExpr) or - not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): - localError(c.config, n.sons[i].info, errNamedExprExpected) + 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.sons[i].kind == nkExprColonExpr: - localError(c.config, n.sons[i].info, errNamedExprNotAllowed) + if n[i].kind == nkExprColonExpr: + localError(c.config, n[i].info, errNamedExprNotAllowed) return paNone -proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = newNodeI(nkTupleConstr, n.info) + var expected: PType = nil + if expectedType != nil: + expected = expectedType.skipTypes(abstractRange-{tyDistinct}) + if not (expected.kind == tyTuple and expected.len == n.len): + expected = nil var typ = newTypeS(tyTuple, c) typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs var ids = initIntSet() - for i in countup(0, sonsLen(n) - 1): - if n[i].kind != nkExprColonExpr or n[i][0].kind notin {nkSym, nkIdent}: - illFormedAst(n.sons[i], c.config) - var id: PIdent - if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident - else: id = n.sons[i].sons[0].sym.name + 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.sons[i].info, errFieldInitTwice % id.s) - n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1], - flags*{efAllowDestructor}) - var f = newSymS(skField, n.sons[i].sons[0], c) - f.typ = skipIntLit(n.sons[i].sons[1].typ) + localError(c.config, n[i].info, errFieldInitTwice % id.s) + # can check if field name matches expected type here + let expectedElemType = if expected != nil: expected[i] else: nil + n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType) + if expectedElemType != nil and + (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)): + # hasEmpty/nil check is to not break existing code like + # `const foo = [(1, {}), (2, {false})]`, + # `const foo = if true: (0, nil) else: (1, new(int))` + n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info) + + if n[i][1].typ.kind == tyTypeDesc: + localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.") + n[i][1].typ = errorType(c) + + var f = newSymS(skField, n[i][0], c) + f.typ = skipIntLit(n[i][1].typ.skipTypes({tySink}), c.idgen) f.position = i rawAddSon(typ, f.typ) - addSon(typ.n, newSymNode(f)) - n.sons[i].sons[0] = newSymNode(f) - addSon(result, n.sons[i]) + typ.n.add newSymNode(f) + n[i][0] = newSymNode(f) + result.add n[i] result.typ = typ -proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = n # we don't modify n, but compute the type: - result.kind = nkTupleConstr + result.transitionSonsKind(nkTupleConstr) + var expected: PType = nil + if expectedType != nil: + expected = expectedType.skipTypes(abstractRange-{tyDistinct}) + if not (expected.kind == tyTuple and expected.len == n.len): + expected = nil var typ = newTypeS(tyTuple, c) # leave typ.n nil! - for i in countup(0, sonsLen(n) - 1): - n.sons[i] = semExprWithType(c, n.sons[i], flags*{efAllowDestructor}) - addSonSkipIntLit(typ, n.sons[i].typ) + for i in 0..<n.len: + let expectedElemType = if expected != nil: expected[i] else: nil + n[i] = semExprWithType(c, n[i], {}, expectedElemType) + if expectedElemType != nil and + (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)): + # hasEmpty/nil check is to not break existing code like + # `const foo = [(1, {}), (2, {false})]`, + # `const foo = if true: (0, nil) else: (1, new(int))` + n[i] = fitNode(c, expectedElemType, n[i], n[i].info) + addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen) result.typ = typ -proc isTupleType(n: PNode): bool = - if n.len == 0: - return false # don't interpret () as type - for i in countup(0, n.len - 1): - if n[i].typ == nil or n[i].typ.kind != tyTypeDesc: - return false - return true - include semobjconstr -proc semBlock(c: PContext, n: PNode): PNode = +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.sons[0].kind != nkEmpty: - var labl = newSymG(skLabel, n.sons[0], c) + if n[0].kind != nkEmpty: + var labl = newSymG(skLabel, n[0], c) if sfGenSym notin labl.flags: addDecl(c, labl) - n.sons[0] = newSymNode(labl, n.sons[0].info) - suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym) - styleCheckDef(c.config, labl) - n.sons[1] = semExpr(c, n.sons[1]) - n.typ = n.sons[1].typ - if isEmptyType(n.typ): n.kind = nkBlockStmt - else: n.kind = nkBlockExpr + elif labl.owner == nil: + labl.owner = 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 = @@ -2176,181 +2984,478 @@ proc semExportExcept(c: PContext, n: PNode): PNode = return n let exceptSet = readExceptSet(c, n) let exported = moduleName.sym - strTableAdd(c.module.tab, exported) - var i: TTabIter - var s = initTabIter(i, exported.tab) - while s != nil: + 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: - strTableAdd(c.module.tab, s) - s = nextIter(i, exported.tab) - result = n + 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 = - var x = newNodeI(n.kind, n.info) + 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.sons[i] - var o: TOverloadIter + 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: - strTableAdd(c.module.tab, s) - x.add(newSymNode(s, a.info)) - var ti: TTabIter - var it = initTabIter(ti, s.tab) - while it != nil: + reexportSym(c, s) + for it in allSyms(c.graph, s): if it.kind in ExportableSymKinds+{skModule}: - strTableAdd(c.module.tab, it) - it = nextIter(ti, s.tab) + reexportSym(c, it) + result.add newSymNode(it, a.info) + specialSyms(c, it) + markUsed(c, n.info, s) else: while s != nil: - if s.kind in ExportableSymKinds+{skModule}: - x.add(newSymNode(s, a.info)) - strTableAdd(c.module.tab, s) + 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) - result = n -proc shouldBeBracketExpr(n: PNode): bool = +proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = + var tupexp = semTuplePositionsConstr(c, n, flags, expectedType) + var isTupleType: bool = false + if tupexp.len > 0: # don't interpret () as type + isTupleType = tupexp[0].typ.kind == tyTypeDesc + # check if either everything or nothing is tyTypeDesc + for i in 1..<tupexp.len: + if isTupleType != (tupexp[i].typ.kind == tyTypeDesc): + return localErrorNode(c, n, tupexp[i].info, "Mixing types and values in tuples is not allowed.") + if isTupleType: # expressions as ``(int, string)`` are reinterpret as type expressions + result = n + var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) + result.typ = makeTypeDesc(c, typ) + else: + result = tupexp + +proc isExplicitGenericCall(c: PContext, n: PNode): bool = + ## checks if a call node `n` is a routine call with explicit generic params + ## + ## the callee node needs to be either an nkBracketExpr or a call to a + ## symchoice of `[]` in which case it will be transformed into nkBracketExpr + ## + ## the LHS of the bracket expr has to either be a symchoice or resolve to + ## a routine symbol + template checkCallee(n: PNode) = + # check subscript LHS, `n` must be mutable + if isSymChoice(n): + result = true + else: + let s = qualifiedLookUp(c, n, {}) + if s != nil and s.kind in routineKinds: + result = true + n = semSymGenericInstantiation(c, n, s) assert n.kind in nkCallKinds - let a = n.sons[0] - if a.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: - let be = newNodeI(nkBracketExpr, n.info) - for i in 1..<a.len: be.add(a[i]) - n.sons[0] = be - return true + 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) -proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = 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 checks = if efNoEvaluateGeneric in flags: - {checkUndeclared, checkPureEnumFields} - elif efInCall in flags: - {checkUndeclared, checkModule, checkPureEnumFields} - else: - {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} - var s = qualifiedLookUp(c, n, checks) + 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) - result = semSym(c, n, s, flags) - if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: + 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, n.sym, flags) + 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 = getSysType(c.graph, n.info, tyNil) + 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: setIntLitType(c.graph, result) - of nkInt8Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt8) - of nkInt16Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt16) - of nkInt32Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt32) - of nkInt64Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt64) - of nkUIntLit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt) - of nkUInt8Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt8) - of nkUInt16Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt16) - of nkUInt32Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt32) - of nkUInt64Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt64) - #of nkFloatLit: - # if result.typ == nil: result.typ = getFloatLitType(result) - of nkFloat32Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat32) - of nkFloat64Lit, nkFloatLit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat64) - of nkFloat128Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat128) + 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: result.typ = getSysType(c.graph, n.info, tyString) - of nkCharLit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyChar) + 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.kind = nkCall - result = semExpr(c, result, flags) + result.transitionSonsKind(nkCall) + result = semExpr(c, result, flags, expectedType) of nkBind: - message(c.config, n.info, warnDeprecated, "bind") - result = semExpr(c, n.sons[0], flags) - of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy: + 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(c.newTypeWithSons(modifier, @[baseType])) + 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} - var s = qualifiedLookUp(c, n.sons[0], mode) + c.isAmbiguous = false + var s = qualifiedLookUp(c, n[0], mode) if s != nil: - #if c.config.cmd == cmdPretty and n.sons[0].kind == nkDotExpr: - # pretty.checkUse(n.sons[0].sons[1].info, s) case s.kind - of skMacro: - if sfImmediate notin s.flags: - result = semDirectOp(c, n, flags) - else: - result = semMacroExpr(c, n, n, s, flags) - of skTemplate: - if sfImmediate notin s.flags: - result = semDirectOp(c, n, flags) - else: - result = semTemplateExpr(c, n, s, flags) + of skMacro, skTemplate: + result = semDirectOp(c, n, flags, expectedType) of skType: # XXX think about this more (``set`` procs) - if n.len == 2: - result = semConv(c, n) - elif contains(c.ambiguousSymbols, s.id) and n.len == 1: - errorUseQualifier(c, n.info, s) + 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: - result = semObjConstr(c, n, flags) - elif s.magic == mNone: result = semDirectOp(c, n, flags) - else: result = semMagic(c, n, s, flags) + 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) - else: result = semMagic(c, n, s, flags) + 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) - elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and - isSymChoice(n[0][0]): - # indirectOp can deal with explicit instantiations; the fixes - # the 'newSeq[T](x)' bug - setGenericParams(c, n.sons[0]) - result = semDirectOp(c, n, flags) - elif isSymChoice(n.sons[0]) or nfDotField in n.flags: - result = semDirectOp(c, n, flags) + 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) + 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) @@ -2360,54 +3465,51 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # This is a "when nimvm" stmt. result = semWhen(c, n, true) else: - result = semExpr(c, result, flags) + result = semExpr(c, result, flags, expectedType) of nkBracketExpr: checkMinSonsLen(n, 1, c.config) - result = semArrayAccess(c, n, flags) + result = semArrayAccess(c, n, flags, expectedType) of nkCurlyExpr: - result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags) + result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags, expectedType) of nkPragmaExpr: var - expr = n[0] 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: - var tupexp = semTuplePositionsConstr(c, n, flags) - if isTupleType(tupexp): - # reinterpret as type - var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) - result.typ = makeTypeDesc(c, typ) - else: - result = tupexp - of paTupleFields: result = semTupleFieldsConstr(c, n, flags) - of paSingle: result = semExpr(c, n.sons[0], flags) - of nkCurly: result = semSetConstr(c, n) - of nkBracket: result = semArrayConstr(c, n, flags) - of nkObjConstr: result = semObjConstr(c, n, flags) - of nkLambdaKinds: result = semLambda(c, n, flags) - of nkDerefExpr: result = semDeref(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.sons[0]) + result = semAddr(c, n[0]) of nkHiddenAddr, nkHiddenDeref: checkSonsLen(n, 1, c.config) - n.sons[0] = semExpr(c, n.sons[0], flags) + n[0] = semExpr(c, n[0], flags, expectedType) of nkCast: result = semCast(c, n) - of nkIfExpr, nkIfStmt: result = semIf(c, n) + of nkIfExpr, nkIfStmt: result = semIf(c, n, flags, expectedType) of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: checkSonsLen(n, 2, c.config) considerGenSyms(c, n) @@ -2421,32 +3523,27 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = checkMinSonsLen(n, 2, c.config) considerGenSyms(c, n) of nkTableConstr: - result = semTableConstr(c, n) - of nkClosedSymChoice, nkOpenSymChoice: - # handling of sym choices is context dependent - # the node is left intact for now - discard - of nkStaticExpr: - result = semStaticExpr(c, n) - of nkAsgn: result = semAsgn(c, n) - of nkBlockStmt, nkBlockExpr: result = semBlock(c, n) - of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags) + 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) - of nkTryStmt: result = semTry(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) - of nkCaseStmt: result = semCase(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: pragma(c, c.p.owner, n, stmtPragmas) + of nkPragma: semPragmaStmt(c, n) of nkIteratorDef: result = semIterator(c, n) of nkProcDef: result = semProc(c, n) of nkFuncDef: result = semFunc(c, n) @@ -2480,20 +3577,37 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export") result = semExportExcept(c, n) of nkPragmaBlock: - result = semPragmaBlock(c, n) + result = semPragmaBlock(c, n, expectedType) of nkStaticStmt: result = semStaticStmt(c, n) of nkDefer: - n.sons[0] = semExpr(c, n.sons[0]) - if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]): + 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.sons[i] = semExpr(c, n.sons[i]) + 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() |