summary refs log tree commit diff stats
path: root/compiler/semexprs.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/semexprs.nim')
pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
parse/0: instruction: 1
parse/0:   ingredient: {name: "34", value: 0, type: 0, properties: [34: literal]}
parse/0:   product: {name: "12", value: 0, type: 1, properties: [12: integer]}
parse/0: instruction: 1
parse/0:   ingredient: {name: "35", value: 0, type: 0, properties: [35: literal]}
parse/0:   product: {name: "13", value: 0, type: 1, properties: [13: integer]}
parse/0: instruction: 19
parse/0:   ingredient: {name: "12", value: 0, type: 4, properties: [12: point]}
parse/0:   ingredient: {name: "1", value: 0, type: 0, properties: [1: offset]}
parse/0:   product: {name: "15", value: 0, type: 2-1, properties: [15: address:integer]}
after-brace/0: recipe main
after-brace/0: copy ...
after-brace/0: copy ...
after-brace/0: get-address ...
run/0: instruction main/0
run/0: ingredient 0 is 34
mem/0: storing 34 in location 12
run/0: instruction main/1
run/0: ingredient 0 is 35
mem/0: storing 35 in location 13
run/0: instruction main/2
run/0: ingredient 0 is 12
run/0: ingredient 1 is 1
run/0: address to copy is 13
run/0: product 0 is 13
mem/0: storing 13 in location 15
s='add'>+ # 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 == EnforceVoidContext: - # we cannot check for 'void' in macros ... - LocalError(n.info, errExprXHasNoType, - renderTree(result, {renderNoComments})) + +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: - # XXX tyGenericInst here? - semProcvarCheck(c, result) - if result.typ.kind == tyVar: result = newDeref(result) - semDestructorCheck(c, result, flags) + 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(n.info, errExprXHasNoType, + localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) result.typ = errorType(c) - else: - semProcvarCheck(c, result) - semDestructorCheck(c, result, flags) proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = symChoice(c, n, s, scClosed) - -proc inlineConst(n: PNode, s: PSym): PNode {.inline.} = - result = copyTree(s.ast) - result.typ = s.typ - result.info = n.info - -proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = - case s.kind - of skConst: - markUsed(n, s) - case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind - of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, - tyTuple, tySet, tyUInt..tyUInt64: - result = inlineConst(n, s) - of tyArrayConstr, tySequence: - # Consider:: - # const x = [] - # proc p(a: openarray[int]) - # proc q(a: openarray[char]) - # p(x) - # q(x) - # - # It is clear that ``[]`` means two totally different things. Thus, we - # copy `x`'s AST into each context, so that the type fixup phase can - # deal with two different ``[]``. - if s.ast.len == 0: result = inlineConst(n, s) - else: result = newSymNode(s, n.info) - else: - result = newSymNode(s, n.info) - of skMacro: result = semMacroExpr(c, n, n, s) - of skTemplate: result = semTemplateExpr(c, n, s) - of skVar, skLet, skResult, skParam, skForVar: - markUsed(n, s) - # if a proc accesses a global variable, it is not side effect free: - if sfGlobal in s.flags: - incl(c.p.owner.flags, sfSideEffect) - elif s.kind == skParam and s.typ.kind == tyExpr and s.typ.n != nil: - # XXX see the hack in sigmatch.nim ... - return s.typ.n - result = newSymNode(s, n.info) - # We cannot check for access to outer vars for example because it's still - # not sure the symbol really ends up being used: - # var len = 0 # but won't be called - # genericThatUsesLen(x) # marked as taking a closure? - of skGenericParam: - if s.typ.kind == tyExpr: - result = newSymNode(s, n.info) - result.typ = s.typ - elif s.ast != nil: - result = semExpr(c, s.ast) - else: - InternalError(n.info, "no default for") - result = emptyNode - of skType: - markUsed(n, s) - result = newSymNode(s, n.info) - result.typ = makeTypeDesc(c, s.typ) + +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: - markUsed(n, s) - result = newSymNode(s, n.info) + result = nil + if not isSym: + # set symchoice node type back to None + n.typ = newTypeS(tyNone, c) + +proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = + if n.kind == nkOpenSymChoice: + result = semOpenSym(c, n, flags, expectedType, + warnDisabled = nfDisabledOpenSym in n.flags and + genericsOpenSym notin c.features) + if result != nil: + return + result = n + resolveSymChoice(c, result, flags, expectedType) + if isSymChoice(result) and result.len == 1: + # resolveSymChoice can leave 1 sym + result = result[0] + if isSymChoice(result) and efAllowSymChoice notin flags: + var err = "ambiguous identifier: '" & result[0].sym.name.s & + "' -- use one of the following:\n" + for child in n: + let candidate = child.sym + err.add " " & candidate.owner.name.s & "." & candidate.name.s + err.add ": " & typeToString(candidate.typ) & "\n" + localError(c.config, n.info, err) + n.typ = errorType(c) + result = n + if result.kind == nkSym: + result = semSym(c, result, result.sym, flags) + +proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} = + result = copyTree(s.astdef) + if result.isNil: + localError(c.config, n.info, "constant of type '" & typeToString(s.typ) & "' has no value") + result = newSymNode(s) + else: + result.typ = s.typ + result.info = n.info type TConvStatus = enum convOK, convNotNeedeed, - convNotLegal + convNotLegal, + convNotInRange -proc checkConversionBetweenObjects(castDest, src: PType): TConvStatus = - return if inheritanceDiff(castDest, src) == high(int): +proc checkConversionBetweenObjects(castDest, src: PType; pointers: int): TConvStatus = + let diff = inheritanceDiff(castDest, src) + return if diff == high(int) or (pointers > 1 and diff != 0): convNotLegal else: convOK -const +const IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64} -proc checkConvertible(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 = skipTypes(src, abstractVar-{tyTypeDesc}) - while (d != nil) and (d.Kind in {tyPtr, tyRef}) and (d.Kind == s.Kind): - d = base(d) - s = base(s) + var d = skipTypes(targetTyp, abstractVar) + var s = srcTyp + if s.kind in tyUserTypeClasses and s.isResolvedUserTypeClass: + s = s.last + s = skipTypes(s, abstractVar-{tyTypeDesc, tyOwned}) + if s.kind == tyOwned and d.kind != tyOwned: + s = s.skipModifier + var pointers = 0 + while (d != nil) and (d.kind in {tyPtr, tyRef, tyOwned}): + if s.kind == tyOwned and d.kind != tyOwned: + s = s.skipModifier + elif d.kind != s.kind: + break + else: + d = d.elementType + s = s.elementType + inc pointers + + let targetBaseTyp = skipTypes(targetTyp, abstractVarRange) + let srcBaseTyp = skipTypes(srcTyp, abstractVarRange-{tyTypeDesc}) + if d == nil: result = convNotLegal - elif d.Kind == tyObject and s.Kind == tyObject: - result = checkConversionBetweenObjects(d, s) - elif (skipTypes(castDest, abstractVarRange).Kind in IntegralTypes) and - (skipTypes(src, abstractVarRange-{tyTypeDesc}).Kind in IntegralTypes): - # accept conversion between integral types + 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: - case cmpTypes(d, s) + 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: - nil + discard -proc isCastable(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 + ## dst.size >= src.size, and typeAllowed(dst, skParam) #const - # castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString, + # castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString, # tySequence, tyPointer, tyNil, tyOpenArray, # tyProc, tySet, tyEnum, tyBool, tyChar} - var ds, ss: biggestInt - # this is very unrestrictive; cast is allowed if castDest.size >= src.size - ds = computeSize(dst) - ss = computeSize(src) - if ds < 0: - result = false - elif ss < 0: - result = false - else: - result = (ds >= ss) or + let src = src.skipTypes(tyUserTypeClasses) + if skipTypes(dst, abstractInst-{tyOpenArray}).kind == tyOpenArray: + return false + if skipTypes(src, abstractInst-{tyTypeDesc}).kind == tyTypeDesc: + return false + if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass: + return false + let conf = c.config + if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}: + let d = skipTypes(dst, abstractInst) + let s = skipTypes(src, abstractInst) + if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal: + return false + elif d.kind in IntegralTypes and s.kind in {tyString, tySequence}: + return false + + var dstSize, srcSize: BiggestInt + dstSize = computeSize(conf, dst) + srcSize = computeSize(conf, src) + if dstSize == -3 or srcSize == -3: # szUnknownSize + # The Nim compiler can't detect if it's legal or not. + # Just assume the programmer knows what he is doing. + return true + if dstSize < 0: + return false + elif srcSize < 0: + return false + elif typeAllowed(dst, skParam, c, {taIsCastable}) != nil: + return false + elif dst.kind == tyProc and dst.callConv == ccClosure: + return src.kind == tyProc and src.callConv == ccClosure + else: + result = (dstSize >= srcSize) or (skipTypes(dst, abstractInst).kind in IntegralTypes) or (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes) - -proc isSymChoice(n: PNode): bool {.inline.} = - result = n.kind in nkSymChoices + if result and src.kind == tyNil: + return dst.size <= conf.target.ptrSize -proc semConv(c: PContext, n: PNode): PNode = - if sonsLen(n) != 2: - LocalError(n.info, errConvNeedsOneArg) +proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = + # XXX: liftParamType started to perform addDecl + # we could do that instead in semTypeNode by snooping for added + # gnrc. params, then it won't be necessary to open a new scope here + openScope(c) + var lifted = liftParamType(c, skType, newNodeI(nkArgList, info), + t, ":anon", info) + closeScope(c) + if lifted != nil: t = lifted + +proc isOwnedSym(c: PContext; n: PNode): bool = + let s = qualifiedLookUp(c, n, {}) + result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned" + +proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode = + if n.len != 2: + localError(c.config, n.info, "a type conversion takes exactly one argument") return n + result = newNodeI(nkConv, n.info) - result.typ = semTypeNode(c, n.sons[0], nil).skipTypes({tyGenericInst}) - addSon(result, copyTree(n.sons[0])) - addSon(result, semExprWithType(c, n.sons[1])) - var op = result.sons[1] - + + var targetType = semTypeNode(c, n[0], nil) + case targetType.skipTypes({tyDistinct}).kind + of tyTypeDesc: + internalAssert c.config, targetType.len > 0 + if targetType.base.kind == tyNone: + return semTypeOf(c, n) + else: + targetType = targetType.base + of tyStatic: + var evaluated = semStaticExpr(c, n[1], expectedType) + if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc: + result = n + result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil) + return + elif targetType.base.kind == tyNone: + return evaluated + else: + targetType = targetType.base + of tyAnything, tyUntyped, tyTyped: + localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType)) + else: discard + + maybeLiftType(targetType, c, n[0].info) + + if targetType.kind in {tySink, tyLent} or isOwnedSym(c, n[0]): + let baseType = semTypeNode(c, n[1], nil).skipTypes({tyTypeDesc}) + let t = newTypeS(targetType.kind, c, baseType) + if targetType.kind == tyOwned: + t.flags.incl tfHasOwned + result = newNodeI(nkType, n.info) + result.typ = makeTypeDesc(c, t) + return + + result.add copyTree(n[0]) + + # special case to make MyObject(x = 3) produce a nicer error message: + if n[1].kind == nkExprEqExpr and + targetType.skipTypes(abstractPtrs).kind == tyObject: + localError(c.config, n.info, "object construction uses ':', not '='") + var op = semExprWithType(c, n[1], flags * {efDetermineType} + {efAllowSymChoice}) + if isSymChoice(op) and op[0].sym.kind notin routineKinds: + # T(foo) disambiguation syntax only allowed for routines + op = semSymChoice(c, op) + if targetType.kind != tyGenericParam and targetType.isMetaType: + let final = inferWithMetatype(c, targetType, op, true) + result.add final + result.typ = final.typ + return + + result.typ = targetType + # XXX op is overwritten later on, this is likely added too early + # here or needs to be overwritten too then. + result.add op + + if targetType.kind == tyGenericParam or + (op.typ != nil and op.typ.kind == tyFromExpr and c.inGenericContext > 0): + # expression is compiled early in a generic body + result.typ = makeTypeFromExpr(c, copyTree(result)) + return result + if not isSymChoice(op): - let status = checkConvertible(result.typ, op.typ) + let status = checkConvertible(c, result.typ, op) case status - of convOK: nil + of convOK: + # handle SomeProcType(SomeGenericProc) + if op.kind == nkSym and op.sym.isGenericRoutine: + result[1] = fitNode(c, result.typ, result[1], result.info) + elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple: + op = fitNode(c, targetType, op, result.info) of convNotNeedeed: - Message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) + if efNoSem2Check notin flags: + message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) of convNotLegal: - LocalError(n.info, errGenerated, MsgKindToString(errIllegalConvFromXtoY)% - [op.typ.typeToString, result.typ.typeToString]) + 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(result.typ, it.typ) - if status == convOK: - markUsed(n, it.sym) + for i in 0..<op.len: + let it = op[i] + let status = checkConvertible(c, result.typ, it) + if status in {convOK, convNotNeedeed}: + markUsed(c, n.info, it.sym) + onUse(n.info, it.sym) markIndirect(c, it.sym) return it - localError(n.info, errUseQualifier, op.sons[0].sym.name.s) - -proc semCast(c: PContext, n: PNode): PNode = - if optSafeCode in gGlobalOptions: localError(n.info, errCastNotInSafeMode) - #incl(c.p.owner.flags, sfSideEffect) - checkSonsLen(n, 2) + errorUseQualifier(c, n.info, op[0].sym) + +proc semCast(c: PContext, n: PNode): PNode = + ## Semantically analyze a casting ("cast[type](param)") + checkSonsLen(n, 2, c.config) + let targetType = semTypeNode(c, n[0], nil) + let castedExpr = semExprWithType(c, n[1]) + if castedExpr.kind == nkClosedSymChoice: + errorUseQualifier(c, n[1].info, castedExpr) + if targetType == nil: + localError(c.config, n.info, "Invalid usage of cast, cast requires a type to convert to, e.g., cast[int](0d).") + if tfHasMeta in targetType.flags: + localError(c.config, n[0].info, "cannot cast to a non concrete type: '$1'" % $targetType) + if not isCastable(c, targetType, castedExpr.typ, n.info): + localError(c.config, n.info, "expression cannot be cast to '$1'" % $targetType) result = newNodeI(nkCast, n.info) - result.typ = semTypeNode(c, n.sons[0], nil) - addSon(result, copyTree(n.sons[0])) - addSon(result, semExprWithType(c, n.sons[1])) - if not isCastable(result.typ, result.sons[1].Typ): - LocalError(result.info, errExprCannotBeCastedToX, - typeToString(result.Typ)) - -proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = - const + result.typ = targetType + result.add copyTree(n[0]) + result.add castedExpr + +proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = + const opToStr: array[mLow..mHigh, string] = ["low", "high"] - if sonsLen(n) != 2: - LocalError(n.info, errXExpectsTypeOrValue, opToStr[m]) - else: - n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) - var typ = skipTypes(n.sons[1].typ, abstractVarRange) - case typ.Kind - of tySequence, tyString, tyOpenArray, tyVarargs: - n.typ = getSysType(tyInt) - of tyArrayConstr, 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) + if n.len != 2: + localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m]) + else: + n[1] = semExprWithType(c, n[1], {efDetermineType}) + var typ = skipTypes(n[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst}) + case typ.kind + of tySequence, tyString, tyCstring, tyOpenArray, tyVarargs: + n.typ = getSysType(c.graph, n.info, tyInt) + of tyArray: + n.typ = typ.indexType + if n.typ.kind == tyRange and emptyRange(n.typ.n[0], n.typ.n[1]): #Invalid range + n.typ = getSysType(c.graph, n.info, tyInt) + of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt..tyUInt64, tyFloat..tyFloat64: + n.typ = n[1].typ.skipTypes({tyTypeDesc}) of tyGenericParam: - # leave it for now, it will be resolved in semtypinst - n.typ = getSysType(tyInt) + # prepare this for resolving in semtypinst: + # we must use copyTree here in order to avoid creating a cycle + # that could easily turn into an infinite recursion in semtypinst + n.typ = makeTypeFromExpr(c, n.copyTree) else: - LocalError(n.info, errInvalidArgForX, opToStr[m]) + localError(c.config, n.info, "invalid argument for: " & opToStr[m]) result = n -proc semSizeof(c: PContext, n: PNode): PNode = - if sonsLen(n) != 2: - LocalError(n.info, errXExpectsTypeOrValue, "sizeof") - else: - n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) - #restoreOldStyleType(n.sons[1]) - n.typ = getSysType(tyInt) - result = n +proc fixupStaticType(c: PContext, n: PNode) = + # This proc can be applied to evaluated expressions to assign + # them a static type. + # + # XXX: with implicit static, this should not be necessary, + # because the output type of operations such as `semConstExpr` + # should be a static type (as well as the type of any other + # expression that can be implicitly evaluated). For now, we + # apply this measure only in code that is enlightened to work + # with static types. + if n.typ.kind != tyStatic: + n.typ = newTypeS(tyStatic, c, n.typ) + n.typ.n = n # XXX: cycles like the one here look dangerous. + # Consider using `n.copyTree` + +proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = + internalAssert c.config, + n.len == 3 and + n[1].typ != nil and + n[2].kind in {nkStrLit..nkTripleStrLit, nkType} -proc semOf(c: PContext, n: PNode): PNode = - if sonsLen(n) == 3: - n.sons[1] = semExprWithType(c, n.sons[1]) - n.sons[2] = semExprWithType(c, n.sons[2], {efDetermineType}) - #restoreOldStyleType(n.sons[1]) - #restoreOldStyleType(n.sons[2]) - let a = skipTypes(n.sons[1].typ, abstractPtrs) - let b = skipTypes(n.sons[2].typ, abstractPtrs) - let x = skipTypes(n.sons[1].typ, abstractPtrs-{tyTypeDesc}) - let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc}) - - if x.kind == tyTypeDesc or y.kind != tyTypeDesc: - LocalError(n.info, errXExpectsObjectTypes, "of") - elif b.kind != tyObject or a.kind != tyObject: - LocalError(n.info, errXExpectsObjectTypes, "of") - else: - let diff = inheritanceDiff(a, b) - # | returns: 0 iff `a` == `b` - # | returns: -x iff `a` is the x'th direct superclass of `b` - # | returns: +x iff `a` is the x'th direct subclass of `b` - # | returns: `maxint` iff `a` and `b` are not compatible at all - if diff <= 0: - # optimize to true: - Message(n.info, hintConditionAlwaysTrue, renderTree(n)) - result = newIntNode(nkIntLit, 1) - result.info = n.info - result.typ = getSysType(tyBool) - return result - elif diff == high(int): - LocalError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a)) - else: - LocalError(n.info, errXExpectsTwoArguments, "of") - n.typ = getSysType(tyBool) - result = n + var + res = false + t1 = n[1].typ + t2 = n[2].typ -proc isOpImpl(c: PContext, n: PNode): PNode = - InternalAssert n.sonsLen == 3 and - n[1].kind == nkSym and n[1].sym.kind == skType and - n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - - let t1 = n[1].sym.typ.skipTypes({tyTypeDesc}) + 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) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator in t.flags)) + res = t.kind == tyProc and + t.callConv == ccClosure and + tfIterator in t.flags + else: + res = false else: - var match: bool - let t2 = n[2].typ - case t2.kind - of tyTypeClasses: - var m: TCandidate - InitCandidate(m, t2) - match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil - of tyOrdinal: - var m: TCandidate - InitCandidate(m, t2) - match = isOrdinalType(t1) - of tySequence, tyArray, tySet: - var m: TCandidate - InitCandidate(m, t2) - match = typeRel(m, t2, t1) != isNone + if t1.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}).kind != tyGenericBody: + maybeLiftType(t2, c, n.info) else: - match = sameType(t1, t2) - - result = newIntNode(nkIntLit, ord(match)) - + #[ + for this case: + type Foo = object[T] + Foo is Foo + ]# + discard + var m = newCandidate(c, t2) + if efExplain in flags: + m.diagnostics = @[] + m.diagnosticsEnabled = true + res = typeRel(m, t2, t1) >= isSubtype # isNone + # `res = sameType(t1, t2)` would be wrong, e.g. for `int is (int|float)` + + result = newIntNode(nkIntLit, ord(res)) result.typ = n.typ -proc semIs(c: PContext, n: PNode): PNode = - if sonsLen(n) != 3: - LocalError(n.info, errXExpectsTwoArguments, "is") +proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = + if n.len != 3 or n[2].kind == nkEmpty: + localError(c.config, n.info, "'is' operator takes 2 arguments") + return errorNode(c, n) + let boolType = getSysType(c.graph, n.info, tyBool) result = n - n.typ = getSysType(tyBool) - - n.sons[1] = semExprWithType(c, n[1], {efDetermineType}) - + n.typ = boolType + var liftLhs = true + + n[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) if n[2].kind notin {nkStrLit..nkTripleStrLit}: let t2 = semTypeNode(c, n[2], nil) - n.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]) - if n[1].typ.kind != tyTypeDesc: - n.sons[1] = makeTypeSymNode(c, n[1].typ, n[1].info) - elif n[1].typ.sonsLen == 0: - # this is a typedesc variable, leave for evals - return + var lhsType = n[1].typ + if lhsType.kind != tyTypeDesc: + if liftLhs: + n[1] = makeTypeSymNode(c, lhsType, n[1].info) + lhsType = n[1].typ + else: + if c.inGenericContext > 0 and lhsType.base.containsUnresolvedType: + # BUGFIX: don't evaluate this too early: ``T is void`` + return - let t1 = n[1].typ.sons[0] - # BUGFIX: don't evaluate this too early: ``T is void`` - if not containsGenericType(t1): result = isOpImpl(c, n) + result = isOpImpl(c, n, flags) proc semOpAux(c: PContext, n: PNode) = - const flags = {efDetermineType} - for i in countup(1, n.sonsLen-1): - var a = n.sons[i] - if a.kind == nkExprEqExpr and sonsLen(a) == 2: - var info = a.sons[0].info - a.sons[0] = newIdentNode(considerAcc(a.sons[0]), 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 = +proc overloadedCallOpr(c: PContext, n: PNode): PNode = # quick check if there is *any* () operator overloaded: - var par = getIdent("()") - if searchInScopes(c, par) == nil: + var par = getIdent(c.cache, "()") + 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(n: PNode, newType: PType, check: bool) = +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(n.sons[i], elemType(newType), check) - of nkPar: - if newType.kind != tyTuple: - InternalError(n.info, "changeType: no tuple type for constructor") - elif newType.n == nil: nil - elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: - for i in countup(0, sonsLen(n) - 1): - var m = n.sons[i].sons[0] - if m.kind != nkSym: - internalError(m.info, "changeType(): invalid tuple constr") - return - var f = getSymFromList(newType.n, m.sym.name) - if f == nil: - internalError(m.info, "changeType(): invalid identifier") + of nkCurly: + for i in 0..<n.len: + if n[i].kind == nkRange: + changeType(c, n[i][0], elemType(newType), check) + changeType(c, n[i][1], elemType(newType), check) + else: + changeType(c, n[i], elemType(newType), check) + of nkBracket: + for i in 0..<n.len: + changeType(c, n[i], elemType(newType), check) + of nkPar, nkTupleConstr: + let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}) + if tup.kind != tyTuple: + if tup.kind == tyObject: return + globalError(c.config, n.info, "no tuple type for constructor") + elif n.len > 0 and n[0].kind == nkExprColonExpr: + # named tuple? + for i in 0..<n.len: + var m = n[i][0] + if m.kind != nkSym: + globalError(c.config, m.info, "invalid tuple constructor") return - changeType(n.sons[i].sons[1], f.typ, check) + if tup.n != nil: + var f = getSymFromList(tup.n, m.sym.name) + if f == nil: + globalError(c.config, m.info, "unknown identifier: " & m.sym.name.s) + return + changeType(c, n[i][1], f.typ, check) + else: + changeType(c, n[i][1], tup[i], check) else: - for i in countup(0, sonsLen(n) - 1): - 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, newType.sons[i], check) - n.sons[i] = a + for i in 0..<n.len: + changeType(c, n[i], tup[i], check) + when false: + var m = n[i] + var a = newNodeIT(nkExprColonExpr, m.info, newType[i]) + a.add newSymNode(newType.n[i].sym) + a.add m + changeType(m, tup[i], check) of nkCharLit..nkUInt64Lit: - if check: + if check and n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType): let value = n.intVal - if value < firstOrd(newType) or value > lastOrd(newType): - LocalError(n.info, errGenerated, "cannot convert " & $value & - " to " & typeToString(newType)) - else: nil + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + localError(c.config, n.info, "cannot convert " & $value & + " to " & typeNameAndDesc(newType)) + of nkFloatLit..nkFloat64Lit: + if check and not floatRangeCheck(n.floatVal, newType): + localError(c.config, n.info, errFloatToString % [$n.floatVal, typeNameAndDesc(newType)]) + of nkSym: + if check and n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType): + let value = n.sym.position + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + localError(c.config, n.info, "cannot convert '" & n.sym.name.s & + "' to '" & typeNameAndDesc(newType) & "'") + else: discard n.typ = newType -proc arrayConstrType(c: PContext, n: PNode): PType = - var typ = newTypeS(tyArrayConstr, c) +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 x = n.sons[0] - var lastIndex: biggestInt = sonsLen(n) - 1 - var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) - 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(tyArrayConstr, 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: - var x = n.sons[0] - var lastIndex: biggestInt = 0 - var indexType = getSysType(tyInt) - if x.kind == nkExprColonExpr and sonsLen(x) == 2: - var idx = semConstExpr(c, x.sons[0]) - lastIndex = getOrdValue(idx) - indexType = idx.typ - x = x.sons[1] - - let yy = semExprWithType(c, x) - var typ = yy.typ - addSon(result, yy) - #var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) - for i in countup(1, sonsLen(n) - 1): - x = n.sons[i] - if x.kind == nkExprColonExpr and sonsLen(x) == 2: - var idx = semConstExpr(c, x.sons[0]) - idx = fitNode(c, indexType, idx) + result.typ = n.typ + expectedBase = n.typ.skipTypes(abstractRange) # include tyDistinct this time + if expectedBase != nil: + case expectedBase.kind + of tyArray: + expectedIndexType = expectedBase[0] + expectedElementType = expectedBase[1] + of tyOpenArray, tySequence: + # typed bracket expressions can also have seq type + expectedElementType = expectedBase[0] + else: discard + var + firstIndex, lastIndex: Int128 = Zero + indexType = getSysType(c.graph, n.info, tyInt) + lastValidIndex = lastOrd(c.config, indexType) + if n.len == 0: + if constructType: + rawAddSon(result.typ, + if expectedElementType != nil and + typeAllowed(expectedElementType, skLet, c) == nil: + expectedElementType + else: + newTypeS(tyEmpty, c)) # needs an empty basetype! + lastIndex = toInt128(-1) + else: + var x = n[0] + if x.kind == nkExprColonExpr and x.len == 2: + var idx = semConstExpr(c, x[0], expectedIndexType) + if not isOrdinalType(idx.typ): + localError(c.config, idx.info, "expected ordinal value for array " & + "index, got '$1'" % renderTree(idx)) + else: + firstIndex = getOrdValue(idx) + lastIndex = firstIndex + indexType = idx.typ + lastValidIndex = lastOrd(c.config, indexType) + x = x[1] + + let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType) + var typ: PType + if constructType: + typ = yy.typ + if expectedElementType == nil: + expectedElementType = typ + else: + typ = expectedElementType + result.add yy + #var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal}) + for i in 1..<n.len: + if lastIndex == lastValidIndex: + let validIndex = makeRangeType(c, toInt64(firstIndex), toInt64(lastValidIndex), n.info, + indexType) + localError(c.config, n.info, "size of array exceeds range of index " & + "type '$1' by $2 elements" % [typeToString(validIndex), $(n.len-i)]) + + x = n[i] + if x.kind == nkExprColonExpr and x.len == 2: + var idx = semConstExpr(c, x[0], indexType) + idx = fitNode(c, indexType, idx, x.info) if lastIndex+1 != getOrdValue(idx): - localError(x.info, errInvalidOrderInArrayConstructor) - x = x.sons[1] - - let xx = semExprWithType(c, x, flags*{efAllowDestructor}) + localError(c.config, x.info, "invalid order in array constructor") + x = x[1] + + 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.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info) - -proc fixAbstractType(c: PContext, n: PNode) = - # XXX finally rewrite that crap! - for i in countup(1, sonsLen(n) - 1): - var it = n.sons[i] - case it.kind - of nkHiddenStdConv, nkHiddenSubConv: - if it.sons[1].kind == nkBracket: - it.sons[1].typ = arrayConstrType(c, it.sons[1]) - #it.sons[1] = semArrayConstr(c, it.sons[1]) - if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: - #if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="): - # debug(n) - - var s = skipTypes(it.sons[1].typ, abstractVar) - if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty: - s = copyType(s, getCurrOwner(), false) - skipTypes(s, abstractVar).sons[1] = elemType( - skipTypes(it.typ, abstractVar)) - it.sons[1].typ = s - elif s.kind == tySequence and s.sons[0].kind == tyEmpty: - s = copyType(s, getCurrOwner(), false) - skipTypes(s, abstractVar).sons[0] = elemType( - skipTypes(it.typ, abstractVar)) - it.sons[1].typ = s - - elif skipTypes(it.sons[1].typ, abstractVar).kind in - {tyNil, tyArrayConstr, tyTuple, tySet}: - var s = skipTypes(it.typ, abstractVar) - changeType(it.sons[1], s, check=true) - n.sons[i] = it.sons[1] - of nkBracket: - # an implicitely constructed array (passed to an open array): - n.sons[i] = semArrayConstr(c, it, {}) - else: - nil - #if (it.typ == nil): - # InternalError(it.info, "fixAbstractType: " & renderTree(it)) - -proc skipObjConv(n: PNode): PNode = - case n.kind - of nkHiddenStdConv, nkHiddenSubConv, nkConv: - if skipTypes(n.sons[1].typ, abstractPtrs).kind in {tyTuple, tyObject}: - result = n.sons[1] - else: - result = n - of nkObjUpConv, nkObjDownConv: result = n.sons[0] - else: result = n - -proc isAssignable(c: PContext, n: PNode): TAssignableResult = + if constructType: + addSonSkipIntLit(result.typ, typ, c.idgen) + for i in 0..<result.len: + result[i] = fitNode(c, typ, result[i], result[i].info) + if constructType: + result.typ.setIndexType( + makeRangeType(c, + toInt64(firstIndex), toInt64(lastIndex), + n.info, indexType)) + +proc fixAbstractType(c: PContext, n: PNode) = + for i in 1..<n.len: + let it = n[i] + if it == nil: + localError(c.config, n.info, "'$1' has nil child at index $2" % [renderTree(n, {renderNoComments}), $i]) + return + # do not get rid of nkHiddenSubConv for OpenArrays, the codegen needs it: + if it.kind == nkHiddenSubConv and + skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}: + if skipTypes(it[1].typ, abstractVar).kind in + {tyNil, tyTuple, tySet} or it[1].isArrayConstr: + var s = skipTypes(it.typ, abstractVar + tyUserTypeClasses) + if s.kind != tyUntyped: + changeType(c, it[1], s, check=true) + n[i] = it[1] + +proc isAssignable(c: PContext, n: PNode): TAssignableResult = result = parampatterns.isAssignable(c.p.owner, n) -proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = - if n.kind == nkHiddenDeref: - checkSonsLen(n, 1) - result = n.sons[0] - else: +proc isUnresolvedSym(s: PSym): bool = + result = s.kind == skGenericParam + if not result and s.typ != nil: + result = tfInferrableStatic in s.typ.flags or + (s.kind == skParam and (s.typ.isMetaType or sfTemplateParam in s.flags)) or + (s.kind == skType and + s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) + +proc hasUnresolvedArgs(c: PContext, n: PNode): bool = + # Checks whether an expression depends on generic parameters that + # don't have bound values yet. E.g. this could happen in situations + # such as: + # type Slot[T] = array[T.size, byte] + # proc foo[T](x: default(T)) + # + # Both static parameter and type parameters can be unresolved. + case n.kind + of nkSym: + return isUnresolvedSym(n.sym) + of nkIdent, nkAccQuoted: + let ident = considerQuotedIdent(c, n) + var amb = false + let sym = searchInScopes(c, ident, amb) + if sym != nil: + return isUnresolvedSym(sym) + else: + return false + else: + for i in 0..<n.safeLen: + if hasUnresolvedArgs(c, n[i]): return true + return false + +proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode = + if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or + sfCompileToCpp in c.module.flags): + checkSonsLen(n, 1, c.config) + result = n[0] + else: result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)) - addSon(result, n) - if isAssignable(c, n) notin {arLValue, arLocalLValue}: - localError(n.info, errVarForOutParamNeeded) + 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: # n.sym.typ can be nil in 'check' mode ... if n.sym.typ != nil and - skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: + skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: incl(n.sym.flags, sfAddrTaken) - result = newHiddenAddrTaken(c, n) - of nkDotExpr: - checkSonsLen(n, 2) - if n.sons[1].kind != nkSym: - internalError(n.info, "analyseIfAddressTaken") + result = newHiddenAddrTaken(c, n, isOutParam) + of nkDotExpr: + checkSonsLen(n, 2, c.config) + if n[1].kind != nkSym: + internalError(c.config, n.info, "analyseIfAddressTaken") return - if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: - incl(n.sons[1].sym.flags, sfAddrTaken) - result = newHiddenAddrTaken(c, n) - of nkBracketExpr: - checkMinSonsLen(n, 1) - if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind != tyVar: - if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken) - result = newHiddenAddrTaken(c, n) - else: - result = newHiddenAddrTaken(c, n) - -proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = - checkMinSonsLen(n, 1) - const - FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, - mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, - mAppendSeqElem, mNewSeq, mReset, mShallowCopy} - + if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: + incl(n[1].sym.flags, sfAddrTaken) + result = newHiddenAddrTaken(c, n, isOutParam) + of nkBracketExpr: + checkMinSonsLen(n, 1, c.config) + if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: + if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken) + result = newHiddenAddrTaken(c, n, isOutParam) + else: + result = newHiddenAddrTaken(c, n, isOutParam) + +proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) = + checkMinSonsLen(n, 1, c.config) + if n[0].typ == nil: + # n[0] might be erroring node in nimsuggest + return + const + FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, + mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, + mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, + mWasMoved} + + template checkIfConverterCalled(c: PContext, n: PNode) = + ## Checks if there is a converter call which wouldn't be checked otherwise + # Call can sometimes be wrapped in a deref + let node = if n.kind == nkHiddenDeref: n[0] else: n + if node.kind == nkHiddenCallConv: + analyseIfAddressTakenInCall(c, node, true) # get the real type of the callee # it may be a proc var with a generic alias type, so we skip over them - var t = n.sons[0].typ.skipTypes({tyGenericInst}) - - 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: - if isAssignable(c, n.sons[i]) notin {arLValue, arLocalLValue}: - if n.sons[i].kind != nkHiddenAddr: - LocalError(n.sons[i].info, errVarForOutParamNeeded) + for i in 1..<n.len: + if i < t.len and t[i] != nil and + skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}: + let it = n[i] + let aa = isAssignable(c, it) + if aa notin {arLValue, arLocalLValue}: + if it.kind != nkHiddenAddr: + if aa == arDiscriminant and c.inUncheckedAssignSection > 0: + discard "allow access within a cast(unsafeAssign) section" + else: + localError(c.config, it.info, errVarForOutParamNeededX % $it) + # Make sure to still check arguments for converters + c.checkIfConverterCalled(n[i]) + # bug #5113: disallow newSeq(result) where result is a 'var T': + if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}: + var arg = n[1] #.skipAddr + if arg.kind == nkHiddenDeref: arg = arg[0] + if arg.kind == nkSym and arg.sym.kind == skResult and + arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}: + localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments})) + return - for i in countup(1, sonsLen(n) - 1): - if n.sons[i].kind == nkHiddenCallConv: - # we need to recurse explicitly here as converters can create nested - # calls and then they wouldn't be analysed otherwise - analyseIfAddressTakenInCall(c, n.sons[i]) - semProcvarCheck(c, n.sons[i]) - if i < sonsLen(t) and - skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: - if n.sons[i].kind != nkHiddenAddr: - n.sons[i] = analyseIfAddressTaken(c, n.sons[i]) - + 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 + # 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]) + 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) + result = semfold.getConstExpr(c.module, call, c.idgen, c.graph) if result.isNil: result = n else: return result - result.typ = semfold.getIntervalType(callee.magic, call) - + + block maybeLabelAsStatic: + # XXX: temporary work-around needed for tlateboundstatic. + # This is certainly not correct, but it will get the job + # done until we have a more robust infrastructure for + # implicit statics. + if n.len > 1: + for i in 1..<n.len: + # see bug #2113, it's possible that n[i].typ for errornous code: + if n[i].typ.isNil or n[i].typ.kind != tyStatic or + tfUnresolved notin n[i].typ.flags: + break maybeLabelAsStatic + n.typ = 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 - {sfForward, sfImportc} * callee.flags == {}: - if sfCompileTime notin callee.flags and - optImplicitStatic notin gOptions: return + if callee.kind == skConst or + {sfNoSideEffect, sfCompileTime} * callee.flags != {} and + {sfForward, sfImportc} * callee.flags == {} and n.typ != nil: + + if callee.kind != skConst and + sfCompileTime notin callee.flags and + optImplicitStatic notin c.config.options: return if callee.magic notin ctfeWhitelist: return - if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine: + + if callee.kind notin {skProc, skFunc, skConverter, skConst} or + callee.isGenericRoutineStrict: return - - if n.typ != nil and not typeAllowed(n.typ, skConst): 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]) + 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, c.module, call, c.p.owner) - if result.isNil: - LocalError(n.info, errCannotInterpretNodeX, renderTree(call)) + 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, c.module, call) - if result.isNil: 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]) - result = evalStaticExpr(c, c.module, a, c.p.owner) +proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = + inc c.inStaticContext + openScope(c) + let a = semExprWithType(c, n, expectedType = expectedType) + closeScope(c) + dec c.inStaticContext + if a.findUnresolvedStatic != nil: return a + result = evalStaticExpr(c.module, c.idgen, c.graph, a, c.p.owner) if result.isNil: - LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) - result = emptyNode + localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n)) + result = c.graph.emptyNode + else: + result = fixupTypeAfterEval(c, result, a) proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, - flags: TExprFlags): 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``. + # to 'skIterator' anymore; skIterator is preferred in sigmatch already + # for typeof support. + # for ``typeof(countup(1,3))``, see ``tests/ttoseq``. result = semOverloadedCall(c, n, nOrig, - {skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}) + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags, expectedType) else: - result = semOverloadedCall(c, n, nOrig, - {skProc, skMethod, skConverter, skMacro, skTemplate}) + result = semOverloadedCall(c, n, nOrig, + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags, expectedType) + if result != nil: - if result.sons[0].kind != nkSym: - InternalError("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: nil + of skMacro, skTemplate: discard else: - if (callee.kind == skIterator) and (callee.id == c.p.owner.id): - LocalError(n.info, errRecursiveDependencyX, callee.name.s) - if sfNoSideEffect notin callee.flags: - if {sfImportc, sfSideEffect} * callee.flags != {}: - incl(c.p.owner.flags, sfSideEffect) - -proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode -proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = + if callee.kind == skIterator and callee.id == c.p.owner.id and + not isClosureIterator(c.p.owner.typ): + localError(c.config, n.info, errRecursiveDependencyIteratorX % callee.name.s) + # error correction, prevents endless for loop elimination in transf. + # See bug #2051: + result[0] = newSymNode(errorSym(c, n)) + elif callee.kind == skIterator: + if efWantIterable in flags: + let typ = newTypeS(tyIterable, c) + rawAddSon(typ, result.typ) + result.typ = typ + +proc resolveIndirectCall(c: PContext; n, nOrig: PNode; + t: PType): TCandidate = + result = initCandidate(c, t) + matches(c, n, nOrig, result) + +proc finishOperand(c: PContext, a: PNode): PNode = + if a.typ.isNil: + result = c.semOperand(c, a, {efDetermineType}) + else: + result = a + # XXX tyGenericInst here? + if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}): + #and tfUnresolved in result.typ.flags: + let owner = result.typ.owner + let err = + # consistent error message with evaltempl/semMacroExpr + if owner != nil and owner.kind in {skTemplate, skMacro}: + errMissingGenericParamsForTemplate % a.renderTree + else: + errProcHasNoConcreteType % a.renderTree + localError(c.config, a.info, err) + considerGenSyms(c, result) + +proc semFinishOperands(c: PContext; n: PNode; isBracketExpr = false) = + # this needs to be called to ensure that after overloading resolution every + # argument has been sem'checked + + # skip the first argument for operands of `[]` since it may be an unresolved + # generic proc, which is handled in semMagic + let start = 1 + ord(isBracketExpr) + for i in start..<n.len: + n[i] = finishOperand(c, n[i]) + +proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = + if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError: + return errorNode(c, n) + if n.typ != nil and n.typ.kind == tyFromExpr and c.inGenericContext > 0: + return n + + result = n + + when defined(nimsuggest): + if c.config.expandProgress: + if c.config.expandLevels == 0: + return n + else: + c.config.expandLevels -= 1 + + let callee = result[0].sym + case callee.kind + of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType) + of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType) + else: + semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut}) + activate(c, result) + fixAbstractType(c, result) + analyseIfAddressTakenInCall(c, result) + if callee.magic != mNone: + result = magicsAfterOverloadResolution(c, result, flags, expectedType) + when false: + if result.typ != nil and + not (result.typ.kind == tySequence and result.elementType.kind == tyEmpty): + liftTypeBoundOps(c, result.typ, n.info) + #result = patchResolvedTypeBoundOp(c, result) + if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone): + # don't fold calls in concepts and typeof + result = evalAtCompileTime(c, result) + +proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = nil - checkMinSonsLen(n, 1) - var prc = n.sons[0] - if n.sons[0].kind == nkDotExpr: - checkSonsLen(n.sons[0], 2) - n.sons[0] = semFieldAccess(c, n.sons[0]) - if n.sons[0].kind == nkDotCall: + checkMinSonsLen(n, 1, c.config) + var prc = n[0] + if n[0].kind == nkDotExpr: + checkSonsLen(n[0], 2, c.config) + let n0 = semFieldAccess(c, n[0], {efIsDotCall}) + if n0.kind == nkDotCall: # it is a static call! - result = n.sons[0] - result.kind = nkCall - for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i]) - return semExpr(c, result, flags) - else: - n.sons[0] = semExpr(c, n.sons[0]) + result = n0 + result.transitionSonsKind(nkCall) + result.flags.incl nfExplicitCall + for i in 1..<n.len: result.add n[i] + return semExpr(c, result, flags, expectedType) + elif n0.typ.kind == tyFromExpr and c.inGenericContext > 0: + # don't make assumptions, entire expression needs to be tyFromExpr + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return + else: + n[0] = n0 + else: + n[0] = semExpr(c, n[0], {efInCall, efAllowSymChoice}) + let t = n[0].typ + if t != nil and t.kind in {tyVar, tyLent}: + n[0] = newDeref(n[0]) + elif isSymChoice(n[0]) and nfDotField notin n.flags: + # overloaded generic procs e.g. newSeq[int] can end up here + return semDirectOp(c, n, flags, expectedType) + + var t: PType = nil + if n[0].typ != nil: + t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct}) + if t != nil and t.kind == tyTypeDesc: + if n.len == 1: return semObjConstr(c, n, flags, expectedType) + return semConv(c, n, flags) + let nOrig = n.copyTree semOpAux(c, n) - 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 - var m: TCandidate - initCandidate(m, t) - matches(c, n, nOrig, m) + let m = resolveIndirectCall(c, n, nOrig, t) if m.state != csMatch: - if c.inCompilesContext > 0: + if c.config.m.errorOutputs == {}: # speed up error generation: - GlobalError(n.Info, errTypeMismatch, "") - return emptyNode + globalError(c.config, n.info, "type mismatch") + return c.graph.emptyNode else: var hasErrorType = false - var msg = msgKindToString(errTypeMismatch) - for i in countup(1, sonsLen(n) - 1): - if i > 1: add(msg, ", ") - let nt = n.sons[i].typ - add(msg, typeToString(nt)) - if nt.kind == tyError: + var msg = "type mismatch: got <" + for i in 1..<n.len: + if i > 1: msg.add(", ") + let nt = n[i].typ + msg.add(typeToString(nt)) + if nt.kind == tyError: hasErrorType = true break if not hasErrorType: - add(msg, ")\n" & msgKindToString(errButExpected) & "\n" & - typeToString(n.sons[0].typ)) - LocalError(n.Info, errGenerated, msg) + 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) - # we assume that a procedure that calls something indirectly - # has side-effects: - if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) - elif t != nil and t.kind == tyTypeDesc: - if n.len == 1: return semObjConstr(c, n, flags) - let destType = t.skipTypes({tyTypeDesc, tyGenericInst}) - 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) + elif result.kind notin nkCallKinds: + # the semExpr() in overloadedCallOpr can even break this condition! + # See bug #904 of how to trigger it: + return result #result = afterCallActions(c, result, nOrig, flags) - fixAbstractType(c, result) - analyseIfAddressTakenInCall(c, result) - if result.sons[0].kind == nkSym and result.sons[0].sym.magic != mNone: - result = magicsAfterOverloadResolution(c, result, flags) - result = evalAtCompileTime(c, result) - -proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = - result = n - let callee = result.sons[0].sym - case callee.kind - of skMacro: result = semMacroExpr(c, result, orig, callee) - of skTemplate: result = semTemplateExpr(c, result, callee) + if result[0].kind == nkSym: + result = afterCallActions(c, result, nOrig, flags, expectedType) else: - semFinishOperands(c, result) - activate(c, result) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) - if callee.magic != mNone: - result = magicsAfterOverloadResolution(c, result, flags) - if c.InTypeClass == 0: - result = evalAtCompileTime(c, result) -proc 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 buildStringify(c: PContext, arg: PNode): PNode = - if arg.typ != nil and - skipTypes(arg.typ, abstractInst-{tyTypeDesc}).kind == tyString: - result = arg - else: - result = newNodeI(nkCall, arg.info) - addSon(result, newIdentNode(getIdent"$", arg.info)) - addSon(result, arg) - -proc semEcho(c: PContext, n: PNode): PNode = - # this really is a macro - checkMinSonsLen(n, 1) - for i in countup(1, sonsLen(n) - 1): - var arg = semExprWithType(c, n.sons[i]) - arg = semExprWithType(c, buildStringify(c, arg)) - n.sons[i] = arg - let t = arg.typ - if (t == nil or t.skipTypes(abstractInst).kind != tyString) and - arg.kind != nkEmpty: - LocalError(n.info, errGenerated, - "implicitly invoked '$' does not return string") - let t = n.sons[0].typ - if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) - result = n - -proc buildEchoStmt(c: PContext, n: PNode): PNode = - # we MUST not check 'n' for semantics again here! +proc buildEchoStmt(c: PContext, n: PNode): PNode = + # we MUST not check 'n' for semantics again here! But for now we give up: result = newNodeI(nkCall, n.info) - var e = StrTableGet(magicsys.systemModule.Tab, getIdent"echo") + let e = systemModuleSym(c.graph, getIdent(c.cache, "echo")) if e != nil: - addSon(result, newSymNode(e)) + result.add(newSymNode(e)) else: - LocalError(n.info, errSystemNeeds, "echo") - addSon(result, errorNode(c, n)) - var arg = buildStringify(c, n) - # problem is: implicit '$' is not checked for semantics yet. So we give up - # and check 'arg' for semantics again: - arg = semExpr(c, arg) - if arg != nil: addSon(result, arg) + result.add localErrorNode(c, n, "system needs: echo") + result.add(n) + result.add(newStrNode(nkStrLit, ": " & n.typ.typeToString)) + result = semExpr(c, result) proc semExprNoType(c: PContext, n: PNode): PNode = + let isPush = c.config.hasHint(hintExtendedContext) + if isPush: pushInfoContext(c.config, n.info) result = semExpr(c, n, {efWantStmt}) - discardCheck(c, result) - -proc isTypeExpr(n: PNode): bool = + discardCheck(c, result, {}) + if isPush: popInfoContext(c.config) + +proc isTypeExpr(n: PNode): bool = case n.kind of nkType, nkTypeOfExpr: result = true of nkSym: result = n.sym.kind == skType else: result = false - -proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, - check: var PNode): PSym = + +proc createSetType(c: PContext; baseType: PType): PType = + assert baseType != nil + result = newTypeS(tySet, c) + rawAddSon(result, baseType) + +proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, + check: var PNode): PSym = # transform in a node that contains the runtime check for the # field, if it is in a case-part... result = nil case r.kind - of nkRecList: - for i in countup(0, sonsLen(r) - 1): - result = lookupInRecordAndBuildCheck(c, n, r.sons[i], field, check) - if result != nil: return - of nkRecCase: - checkMinSonsLen(r, 2) - if (r.sons[0].kind != nkSym): IllFormedAst(r) - result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check) - if result != nil: return - var s = newNodeI(nkCurly, r.info) - for i in countup(1, sonsLen(r) - 1): - var it = r.sons[i] + of nkRecList: + for i in 0..<r.len: + result = lookupInRecordAndBuildCheck(c, n, r[i], field, check) + if result != nil: return + of nkRecCase: + checkMinSonsLen(r, 2, c.config) + if (r[0].kind != nkSym): illFormedAst(r, c.config) + result = lookupInRecordAndBuildCheck(c, n, r[0], field, check) + if result != nil: return + let setType = createSetType(c, r[0].typ) + var s = newNodeIT(nkCurly, r.info, setType) + for i in 1..<r.len: + var it = r[i] case it.kind - of nkOfBranch: + of nkOfBranch: result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check) - if result == nil: - for j in 0..sonsLen(it)-2: addSon(s, copyTree(it.sons[j])) - else: - if check == nil: + if result == nil: + for j in 0..<it.len-1: s.add copyTree(it[j]) + else: + if check == nil: check = newNodeI(nkCheckedFieldExpr, n.info) - addSon(check, ast.emptyNode) # make space for access node - s = newNodeI(nkCurly, n.info) - for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j])) - var inExpr = newNodeI(nkCall, n.info) - addSon(inExpr, newIdentNode(getIdent("in"), n.info)) - addSon(inExpr, copyTree(r.sons[0])) - addSon(inExpr, s) #writeln(output, renderTree(inExpr)); - addSon(check, semExpr(c, inExpr)) - return - of nkElse: + check.add c.graph.emptyNode # make space for access node + s = newNodeIT(nkCurly, n.info, setType) + for j in 0..<it.len - 1: s.add copyTree(it[j]) + var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) + inExpr.add newSymNode(getSysMagic(c.graph, n.info, "contains", mInSet), n.info) + inExpr.add s + inExpr.add copyTree(r[0]) + check.add inExpr + #check.add semExpr(c, inExpr) + return + of nkElse: result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check) - if result != nil: - if check == nil: + if result != nil: + if check == nil: check = newNodeI(nkCheckedFieldExpr, n.info) - addSon(check, ast.emptyNode) # make space for access node - var inExpr = newNodeI(nkCall, n.info) - addSon(inExpr, newIdentNode(getIdent("in"), n.info)) - addSon(inExpr, copyTree(r.sons[0])) - addSon(inExpr, s) - var notExpr = newNodeI(nkCall, n.info) - addSon(notExpr, newIdentNode(getIdent("not"), n.info)) - addSon(notExpr, inExpr) - addSon(check, semExpr(c, notExpr)) - return - else: illFormedAst(it) - of nkSym: + check.add c.graph.emptyNode # make space for access node + var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) + inExpr.add newSymNode(getSysMagic(c.graph, n.info, "contains", mInSet), n.info) + inExpr.add s + inExpr.add copyTree(r[0]) + var notExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) + notExpr.add newSymNode(getSysMagic(c.graph, n.info, "not", mNot), n.info) + notExpr.add inExpr + check.add notExpr + return + else: illFormedAst(it, c.config) + of nkSym: if r.sym.name.id == field.id: result = r.sym - else: illFormedAst(n) - -proc makeDeref(n: PNode): PNode = - var t = skipTypes(n.typ, {tyGenericInst}) - result = n - if t.kind == tyVar: - result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) - addSon(result, n) - t = skipTypes(t.sons[0], {tyGenericInst}) - while t.kind in {tyPtr, tyRef}: - var a = result - result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) - addSon(result, a) - t = skipTypes(t.sons[0], {tyGenericInst}) - -proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = - ## returns nil if it's not a built-in field access - checkSonsLen(n, 2) - # early exit for this; see tests/compile/tbindoverload.nim: - if isSymChoice(n.sons[1]): return + else: illFormedAst(n, c.config) + +const + tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} + tyDotOpTransparent = {tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink} + +proc readTypeParameter(c: PContext, typ: PType, + paramName: PIdent, info: TLineInfo): PNode = + # Note: This function will return emptyNode when attempting to read + # a static type parameter that is not yet resolved (e.g. this may + # happen in proc signatures such as `proc(x: T): array[T.sizeParam, U]` + if typ.kind in {tyUserTypeClass, tyUserTypeClassInst}: + for statement in typ.n: + case statement.kind + of nkTypeSection: + for def in statement: + if def[0].sym.name.id == paramName.id: + # XXX: Instead of lifting the section type to a typedesc + # here, we could try doing it earlier in semTypeSection. + # This seems semantically correct and then we'll be able + # to return the section symbol directly here + let foundType = makeTypeDesc(c, def[2].typ) + return newSymNode(copySym(def[0].sym, c.idgen).linkTo(foundType), info) + + of nkConstSection: + for def in statement: + if def[0].sym.name.id == paramName.id: + return def[2] + + else: + discard + + if typ.kind != tyUserTypeClass: + let ty = if typ.kind == tyCompositeTypeClass: typ.firstGenericParam.skipGenericAlias + else: typ.skipGenericAlias + let tbody = ty[0] + for s in 0..<tbody.len-1: + let tParam = tbody[s] + if tParam.sym.name.id == paramName.id: + let rawTyp = ty[s + 1] + if rawTyp.kind == tyStatic: + if rawTyp.n != nil: + return rawTyp.n + else: + return c.graph.emptyNode + else: + let foundTyp = makeTypeDesc(c, rawTyp) + return newSymNode(copySym(tParam.sym, c.idgen).linkTo(foundTyp), info) + + return nil + +proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = + result = nil + assert n.kind in nkIdentKinds + {nkDotExpr} + let s = getGenSym(c, sym) + case s.kind + of skConst: + if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess + markUsed(c, n.info, s) + onUse(n.info, s) + let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc}) + case typ.kind + of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, + tyTuple, tySet, tyUInt..tyUInt64: + if s.magic == mNone: result = inlineConst(c, n, s) + else: result = newSymNode(s, n.info) + of tyArray, tySequence: + # Consider:: + # const x = [] + # proc p(a: openarray[int]) + # proc q(a: openarray[char]) + # p(x) + # q(x) + # + # It is clear that ``[]`` means two totally different things. Thus, we + # copy `x`'s AST into each context, so that the type fixup phase can + # deal with two different ``[]``. + if s.astdef.safeLen == 0: result = inlineConst(c, n, s) + else: result = newSymNode(s, n.info) + of tyStatic: + if typ.n != nil: + result = typ.n + result.typ = typ.base + else: + result = newSymNode(s, n.info) + else: + result = newSymNode(s, n.info) + of skMacro, skTemplate: + # check if we cannot use alias syntax (no required args or generic params) + if sfNoalias in s.flags: + let info = getCallLineInfo(n) + markUsed(c, info, s) + onUse(info, s) + result = symChoice(c, n, s, scClosed) + else: + case s.kind + of skMacro: result = semMacroExpr(c, n, n, s, flags) + of skTemplate: result = semTemplateExpr(c, n, s, flags) + else: discard # unreachable + of skParam: + markUsed(c, n.info, s) + onUse(n.info, s) + if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil: + # XXX see the hack in sigmatch.nim ... + return s.typ.n + elif sfGenSym in s.flags: + # the owner should have been set by now by addParamOrResult + internalAssert c.config, s.owner != nil + result = newSymNode(s, n.info) + of skVar, skLet, skResult, skForVar: + if s.magic == mNimvm: + localError(c.config, n.info, "illegal context for 'nimvm' magic") + + if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess + markUsed(c, n.info, s) + onUse(n.info, s) + result = newSymNode(s, n.info) + # We cannot check for access to outer vars for example because it's still + # not sure the symbol really ends up being used: + # var len = 0 # but won't be called + # genericThatUsesLen(x) # marked as taking a closure? + if hasWarn(c.config, warnResultUsed): + message(c.config, n.info, warnResultUsed) + + of skGenericParam: + onUse(n.info, s) + if s.typ.kind == tyStatic: + result = newSymNode(s, n.info) + result.typ = s.typ + elif s.ast != nil: + result = semExpr(c, s.ast) + else: + n.typ = s.typ + return n + of skType: + if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess + markUsed(c, n.info, s) + onUse(n.info, s) + if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil: + return s.typ.n + result = newSymNode(s, n.info) + result.typ = makeTypeDesc(c, s.typ) + of skField: + # old code, not sure if it's live code: + markUsed(c, n.info, s) + onUse(n.info, s) + result = newSymNode(s, n.info) + of skModule: + # make sure type is None and not nil for discard checking + if efWantStmt in flags: s.typ = newTypeS(tyNone, c) + markUsed(c, n.info, s) + onUse(n.info, s) + result = newSymNode(s, n.info) + else: + let info = getCallLineInfo(n) + #if efInCall notin flags: + markUsed(c, info, s) + onUse(info, s) + result = newSymNode(s, info) + +proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode = + case t.kind + of tyGenericInst: + result = readTypeParameter(c, t, i, n.info) + if result == c.graph.emptyNode: + if c.inGenericContext > 0: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + else: + result = nil + of tyUserTypeClasses: + if t.isResolvedUserTypeClass: + result = readTypeParameter(c, t, i, n.info) + elif c.inGenericContext > 0: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) + else: + result = nil + of tyGenericBody, tyCompositeTypeClass: + if c.inGenericContext > 0: + result = readTypeParameter(c, t, i, n.info) + if result != nil: + # generic parameter exists, stop here but delay until instantiation + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) + else: + result = nil + elif c.inGenericContext > 0 and t.containsUnresolvedType: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) + else: + result = nil - var s = qualifiedLookup(c, n, {checkAmbiguity, checkUndeclared}) +proc tryReadingTypeField(c: PContext, n: PNode, i: PIdent, ty: PType): PNode = + result = nil + var ty = ty.skipTypes(tyDotOpTransparent) + case ty.kind + of tyEnum: + # look up if the identifier belongs to the enum: + var f = PSym(nil) + while ty != nil: + f = getSymFromList(ty.n, i) + if f != nil: break + ty = ty[0] # enum inheritance + if f != nil: + result = newSymNode(f) + result.info = n.info + result.typ = ty + markUsed(c, n.info, f) + onUse(n.info, f) + of tyObject, tyTuple: + if ty.n != nil and ty.n.kind == nkRecList: + let field = lookupInRecord(ty.n, i) + if field != nil: + n.typ = makeTypeDesc(c, field.typ) + result = n + of tyGenericInst: + result = tryReadingTypeField(c, n, i, ty.skipModifier) + if result == nil: + result = tryReadingGenericParam(c, n, i, ty) + else: + result = tryReadingGenericParam(c, n, i, ty) + +proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = + ## returns nil if it's not a built-in field access + checkSonsLen(n, 2, c.config) + # tests/bind/tbindoverload.nim wants an early exit here, but seems to + # work without now. template/tsymchoicefield doesn't like an early exit + # here at all! + #if isSymChoice(n[1]): return + when defined(nimsuggest): + if c.config.cmd == cmdIdeTools: + suggestExpr(c, n) + if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n) + + var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule}) if s != nil: - return semSym(c, n, s, flags) + if s.kind in OverloadableSyms: + result = symChoice(c, n, s, scClosed) + if result.kind == nkSym: result = semSym(c, n, s, flags) + else: + markUsed(c, n[1].info, s) + result = semSym(c, n, s, flags) + onUse(n[1].info, s) + return - n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) - #restoreOldStyleType(n.sons[0]) - var i = considerAcc(n.sons[1]) - 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 - if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.len == 1: - if ty.kind == tyTypeDesc: ty = ty.sons[0] - case ty.kind - of tyEnum: - # look up if the identifier belongs to the enum: - while ty != nil: - f = getSymFromList(ty.n, i) - if f != nil: break - ty = ty.sons[0] # enum inheritance - if f != nil: - result = newSymNode(f) - result.info = n.info - result.typ = ty - markUsed(n, f) - return - of tyGenericInst: - assert ty.sons[0].kind == tyGenericBody - let tbody = ty.sons[0] - for s in countup(0, tbody.len-2): - let tParam = tbody.sons[s] - if tParam.sym.name == i: - let rawTyp = ty.sons[s + 1] - if rawTyp.kind == tyExpr: - return rawTyp.n - else: - let foundTyp = makeTypeDesc(c, rawTyp) - return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info) - return + + if ty.kind == tyTypeDesc: + if ty.base.kind == tyNone: + # This is a still unresolved typedesc parameter. + # If this is a regular proc, then all bets are off and we must return + # tyFromExpr, but when this happen in a macro this is not a built-in + # field access and we leave the compiler to compile a normal call: + if getCurrOwner(c).kind != skMacro: + n.typ = makeTypeFromExpr(c, n.copyTree) + flags.incl efCannotBeDotCall + return n + else: + return nil else: - # echo "TYPE FIELD ACCESS" - # debug 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 - - ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef}) + flags.incl efCannotBeDotCall + return tryReadingTypeField(c, n, i, ty.base) + elif isTypeExpr(n.sons[0]): + flags.incl efCannotBeDotCall + return tryReadingTypeField(c, n, i, ty) + elif ty.kind == tyError: + # a type error doesn't have any builtin fields + return nil + + if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: + ty = ty.last + ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink, tyStatic}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst, tyAlias}) var check: PNode = nil - if ty.kind == tyObject: - while true: + 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], {tyGenericInst}) + if f != nil: break + 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? - 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 - markUsed(n, f) - if check == nil: + if check == nil: result = n - else: - check.sons[0] = n + else: + check[0] = n check.typ = n.typ result = check - elif ty.kind == tyTuple and ty.n != nil: + elif ty.kind == tyTuple and ty.n != nil: f = getSymFromList(ty.n, i) if f != nil: - 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 - markUsed(n, f) + + # we didn't find any field, let's look for a generic param + if result == nil: + let t = n[0].typ.skipTypes(tyDotOpTransparent) + result = tryReadingGenericParam(c, n, i, t) + flags.incl efCannotBeDotCall proc dotTransformation(c: PContext, n: PNode): PNode = - if isSymChoice(n.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 = considerAcc(n.sons[1]) + var i = considerQuotedIdent(c, n[1], n) result = newNodeI(nkDotCall, n.info) - result.flags.incl nfDelegate - addSon(result, newIdentNode(i, n[1].info)) - addSon(result, copyTree(n[0])) - -proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = + result.flags.incl nfDotField + result.add newIdentNode(i, n[1].info) + result.add copyTree(n[0]) + +proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # this is difficult, because the '.' is used in many different contexts - # in Nimrod. We first allow types in the semantic checking. - result = builtinFieldAccess(c, n, flags) - if result == nil: + # in Nim. We first allow types in the semantic checking. + var f = flags - {efIsDotCall} + result = builtinFieldAccess(c, n, f) + if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and + efIsDotCall in flags and callOperator notin c.features and + efCannotBeDotCall notin f): result = dotTransformation(c, n) proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = result = newNodeI(nkCall, n.info) result.add(newIdentNode(ident, n.info)) - for i in 0 .. n.len-1: result.add(n[i]) - -proc semDeref(c: PContext, n: PNode): PNode = - checkSonsLen(n, 1) - n.sons[0] = semExprWithType(c, n.sons[0]) + for s in n: result.add s + +proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode = + checkSonsLen(n, 1, c.config) + n[0] = semExprWithType(c, n[0]) + let a = getConstExpr(c.module, n[0], c.idgen, c.graph) + if a != nil: + if a.kind == nkNilLit and efInTypeof notin flags: + localError(c.config, n.info, "nil dereference is not allowed") + n[0] = a result = n - var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar}) + var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned}) case t.kind - of tyRef, tyPtr: n.typ = t.sons[0] + 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: - var 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) - n.sons[0] = semExprWithType(c, n.sons[0]) - var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) + checkMinSonsLen(n, 2, c.config) + # signal that generic parameters may be applied after + n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric, efAllowSymChoice}) + var arr = skipTypes(n[0].typ, {tyGenericInst, tyUserTypeClassInst, tyOwned, + tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) + if arr.kind == tyStatic: + if arr.base.kind == tyNone: + result = n + result.typ = semStaticType(c, n[1], nil) + return + elif arr.n != nil: + return semSubscript(c, arr.n, flags) + else: + arr = arr.base + case arr.kind - of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, 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(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 + # The result so far is a tyTypeDesc bound # a tyGenericBody. The line below will substitute # it with the instantiated type. result = n result.typ = makeTypeDesc(c, semTypeNode(c, n, nil)) #result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) - of tyTuple: - checkSonsLen(n, 2) - n.sons[0] = makeDeref(n.sons[0]) + of tyTuple: + if n.len != 2: return nil + 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}).kind in - {tyInt..tyInt64}: - var idx = getOrdValue(n.sons[1]) - if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)] - else: LocalError(n.info, errInvalidIndexValueForTuple) - else: - LocalError(n.info, errIndexTypesDoNotMatch) - result = n - else: nil - -proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = + n[1] = semConstExpr(c, n[1]) + if skipTypes(n[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in + {tyInt..tyInt64}: + let idx = getOrdValue(n[1]) + if idx >= 0 and idx < arr.len: n.typ = arr[toInt(idx)] + else: + localError(c.config, n.info, + "invalid index $1 in subscript for tuple of length $2" % + [$idx, $arr.len]) + result = n + else: + result = nil + else: + let s = if n[0].kind == nkSym: n[0].sym + elif n[0].kind in nkSymChoices + {nkOpenSym}: n[0][0].sym + else: nil + if s != nil: + case s.kind + of skProc, skFunc, skMethod, skConverter, skIterator: + # type parameters: partial generic specialization + n[0] = semSymGenericInstantiation(c, n[0], s) + result = maybeInstantiateGeneric(c, n, s) + of skMacro, skTemplate: + if efInCall in flags: + # We are processing macroOrTmpl[] in macroOrTmpl[](...) call. + # Return as is, so it can be transformed into complete macro or + # template call in semIndirectOp caller. + result = n + else: + # We are processing macroOrTmpl[] not in call. Transform it to the + # macro or template call with generic arguments here. + n.transitionSonsKind(nkCall) + case s.kind + of skMacro: result = semMacroExpr(c, n, n, s, flags) + of skTemplate: result = semTemplateExpr(c, n, s, flags) + else: discard + of skType: + result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) + else: + discard + +proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = semSubscript(c, n, flags) if result == nil: # overloaded [] operator: - result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]")) + result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")), flags, expectedType) proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = - var id = considerAcc(a[1]) - let setterId = newIdentNode(getIdent(id.s & '='), n.info) + var id = considerQuotedIdent(c, a[1], a) + var setterId = newIdentNode(getIdent(c.cache, id.s & '='), n.info) # a[0] is already checked for semantics, that does ``builtinFieldAccess`` # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # nodes? let aOrig = nOrig[0] - result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])]) - let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]]) + result = newTreeI(nkCall, n.info, setterId, a[0], n[1]) + result.flags.incl nfDotSetter + let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1]) result = semOverloadedCallAnalyseEffects(c, result, orig, {}) - + if result != nil: result = afterCallActions(c, result, nOrig, {}) #fixAbstractType(c, result) #analyseIfAddressTakenInCall(c, result) -proc takeImplicitAddr(c: PContext, n: PNode): PNode = +proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = + # See RFC #7373, calls returning 'var T' are assumed to + # return a view into the first argument (if there is one): + let root = exprRoot(n) + if root != nil and root.owner == c.p.owner: + template url: string = "var_t_return.html".createDocLink + if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags: + localError(c.config, n.info, "'$1' escapes its stack frame; context: '$2'; see $3" % [ + root.name.s, renderTree(n, {renderNoComments}), url]) + elif root.kind == skParam and root.position != 0: + localError(c.config, n.info, "'$1' is not the first parameter; context: '$2'; see $3" % [ + root.name.s, renderTree(n, {renderNoComments}), url]) case n.kind of nkHiddenAddr, nkAddr: return n - of nkHiddenDeref, nkDerefExpr: return n.sons[0] + of nkDerefExpr: return n[0] of nkBracketExpr: - if len(n) == 1: return n.sons[0] - else: nil - var valid = isAssignable(c, n) + 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: - LocalError(n.info, errXStackEscape, renderTree(n, {renderNoComments})) + if valid in {arAddressableConst, arLentValue} and isLent: + discard "ok" + elif valid == arLocalLValue: + localError(c.config, n.info, errXStackEscape % renderTree(n, {renderNoComments})) else: - LocalError(n.info, errExprHasNoAddress) - result = newNodeIT(nkHiddenAddr, n.info, makePtrType(c, n.typ)) + localError(c.config, n.info, errExprHasNoAddress) + result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ)) + if n.typ.kind in {tyVar, tyLent}: + n.typ = n.typ.elementType result.add(n) - + proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: - var x = le.sons[0] - if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult: - n.sons[0] = x # 'result[]' --> 'result' - n.sons[1] = takeImplicitAddr(c, ri) - -proc semAsgn(c: PContext, n: PNode): PNode = - checkSonsLen(n, 2) - var a = n.sons[0] + var x = le[0] + if x.kind == nkSym: + if x.sym.kind == skResult and (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView): + n[0] = x # 'result[]' --> 'result' + n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) + x.typ.flags.incl tfVarIsPtr + #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info + elif sfGlobal in x.sym.flags: + x.typ.flags.incl tfVarIsPtr + +proc borrowCheck(c: PContext, n, le, ri: PNode) = + const + PathKinds0 = {nkDotExpr, nkCheckedFieldExpr, + nkBracketExpr, nkAddr, nkHiddenAddr, + nkObjDownConv, nkObjUpConv} + PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv} + + proc getRoot(n: PNode; followDeref: bool): PNode = + result = n + while true: + case result.kind + of nkDerefExpr, nkHiddenDeref: + if followDeref: result = result[0] + else: break + of PathKinds0: + result = result[0] + of PathKinds1: + result = result[1] + else: break + + proc scopedLifetime(c: PContext; ri: PNode): bool {.inline.} = + let n = getRoot(ri, followDeref = false) + result = (ri.kind in nkCallKinds+{nkObjConstr}) or + (n.kind == nkSym and n.sym.owner == c.p.owner and n.sym.kind != skResult) + + proc escapes(c: PContext; le: PNode): bool {.inline.} = + # param[].foo[] = self definitely escapes, we don't need to + # care about pointer derefs: + let n = getRoot(le, followDeref = true) + result = n.kind == nkSym and n.sym.kind == skParam + + # Special typing rule: do not allow to pass 'owned T' to 'T' in 'result = x': + const absInst = abstractInst - {tyOwned} + if ri.typ != nil and ri.typ.skipTypes(absInst).kind == tyOwned and + le.typ != nil and le.typ.skipTypes(absInst).kind != tyOwned and + scopedLifetime(c, ri): + if le.kind == nkSym and le.sym.kind == skResult: + localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " & + "use 'owned(" & typeToString(le.typ) & ")' as the return type") + elif escapes(c, le): + localError(c.config, n.info, + "assignment produces a dangling ref: the unowned ref lives longer than the owned ref") + +template resultTypeIsInferrable(typ: PType): untyped = + typ.isMetaType and typ.kind != tyTypeDesc + +proc goodLineInfo(arg: PNode): TLineInfo = + if arg.kind == nkStmtListExpr and arg.len > 0: + goodLineInfo(arg[^1]) + else: + arg.info + +proc makeTupleAssignments(c: PContext; n: PNode): PNode = + ## expand tuple unpacking assignment into series of assignments + ## + ## mirrored with semstmts.makeVarTupleSection + let lhs = n[0] + let value = semExprWithType(c, n[1], {efTypeAllowed}) + if value.typ.kind != tyTuple: + localError(c.config, n[1].info, errTupleUnpackingTupleExpected % + [typeToString(value.typ, preferDesc)]) + elif lhs.len != value.typ.len: + localError(c.config, n.info, errTupleUnpackingDifferentLengths % + [$lhs.len, typeToString(value.typ, preferDesc), $value.typ.len]) + result = newNodeI(nkStmtList, n.info) + + let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info) + temp.typ = value.typ + temp.flags.incl(sfGenSym) + var v = newNodeI(nkLetSection, value.info) + let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info) + var vpart = newNodeI(nkIdentDefs, v.info, 3) + vpart[0] = tempNode + vpart[1] = c.graph.emptyNode + vpart[2] = value + v.add vpart + result.add(v) + + for i in 0..<lhs.len: + if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore): + # skip _ assignments if we are using a temp as they are already evaluated + discard + else: + result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i)) + +proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = + checkSonsLen(n, 2, c.config) + var a = n[0] case a.kind of nkDotExpr: # r.f = x # --> `f=` (r, x) let nOrig = n.copyTree - 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 @@ -1126,84 +2016,114 @@ proc semAsgn(c: PContext, n: PNode): 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"[]=") - add(result, n[1]) - return semExprNoType(c, result) + result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]=")) + result.add(n[1]) + if mode == noOverloadedSubscript: + bracketNotFoundError(c, result, {}) + return errorNode(c, n) + else: + result = semExprNoType(c, result) + return result of nkCurlyExpr: # a{i} = x --> `{}=`(a, i, x) - result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") - 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 or a.kind == nkTupleConstr: + # unfortunately we need to rewrite ``(x, y) = foo()`` already here so + # that overloading of the assignment operator still works. Usually we + # prefer to do these rewritings in transf.nim: + return semStmt(c, makeTupleAssignments(c, n), {}) + else: + a = semExprWithType(c, a, {efLValue}) else: a = semExprWithType(c, a, {efLValue}) - n.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}).kind != tyVar and - IsAssignable(c, a) == arNone: + 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(a.info, errXCannotBeAssignedTo, + 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: - n.typ = EnforceVoidContext - if lhs.sym.typ.kind == tyGenericParam: - if matchTypeClass(lhs.typ, rhs.typ): - InternalAssert c.p.resultSym != nil - lhs.typ = rhs.typ - c.p.resultSym.typ = rhs.typ - c.p.owner.typ.sons[0] = rhs.typ + let lhs = n[0] + let rhs = semExprWithType(c, n[1], {efTypeAllowed}, le) + if lhs.kind == nkSym and lhs.sym.kind == skResult: + n.typ = c.enforceVoidContext + if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ): + var rhsTyp = rhs.typ + if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass: + rhsTyp = rhsTyp.last + if lhs.sym.typ.kind == tyAnything: + rhsTyp = rhsTyp.skipTypes({tySink}).skipIntLit(c.idgen) + if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}: + internalAssert c.config, c.p.resultSym != nil + # Make sure the type is valid for the result variable + typeAllowedCheck(c, n.info, rhsTyp, skResult) + lhs.typ = rhsTyp + c.p.resultSym.typ = rhsTyp + c.p.owner.typ.setReturnType rhsTyp else: - typeMismatch(n, lhs.typ, rhs.typ) + typeMismatch(c.config, n.info, lhs.typ, rhsTyp, rhs) + borrowCheck(c, n, lhs, rhs) + + n[1] = fitNode(c, le, rhs, goodLineInfo(n[1])) + when false: liftTypeBoundOps(c, lhs.typ, lhs.info) - n.sons[1] = fitNode(c, le, rhs) 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 = +proc semReturn(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 1) - if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or - (c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure): - if n.sons[0].kind != nkEmpty: - # transform ``return expr`` to ``result = expr; return`` - if c.p.resultSym != nil: - var a = newNodeI(nkAsgn, n.sons[0].info) - addSon(a, newSymNode(c.p.resultSym)) - addSon(a, n.sons[0]) - n.sons[0] = semAsgn(c, a) - # optimize away ``result = result``: - if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym: - n.sons[0] = ast.emptyNode + checkSonsLen(n, 1, c.config) + if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or + (not c.p.owner.typ.isNil and isClosureIterator(c.p.owner.typ)): + if n[0].kind != nkEmpty: + if n[0].kind == nkAsgn and n[0][0].kind == nkSym and c.p.resultSym == n[0][0].sym: + discard "return is already transformed" + elif c.p.resultSym != nil: + # transform ``return expr`` to ``result = expr; return`` + var a = newNodeI(nkAsgn, n[0].info) + a.add newSymNode(c.p.resultSym) + a.add n[0] + n[0] = a else: - LocalError(n.info, errNoReturnTypeDeclared) + 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(n.info, errXNotAllowedHere, "\'return\'") + 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): - # transform ``expr`` to ``result = expr``, but not if the expr is already - # ``result``: - if result.kind == nkSym and result.sym == c.p.resultSym: - nil - elif result.kind == nkNilLit: + if result.kind == nkNilLit: # or ImplicitlyDiscardable(result): # new semantic: 'result = x' triggers the void context result.typ = nil @@ -1212,119 +2132,159 @@ proc semProcBody(c: PContext, n: PNode): PNode = # nil # # comment # are not expressions: - fixNilType(result) + 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.setReturnType nil + else: + localError(c.config, c.p.resultSym.info, errCannotInferReturnType % + c.p.owner.name.s) + if isIterator(c.p.owner.typ) and c.p.owner.typ.returnType != nil and + c.p.owner.typ.returnType.kind == tyAnything: + localError(c.config, c.p.owner.info, errCannotInferReturnType % + c.p.owner.name.s) closeScope(c) -proc SemYieldVarResult(c: PContext, n: PNode, restype: PType) = - var t = skipTypes(restype, {tyGenericInst}) +proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = + var t = skipTypes(restype, {tyGenericInst, tyAlias, tySink}) case t.kind - of tyVar: - n.sons[0] = takeImplicitAddr(c, n.sons[0]) + of tyVar, tyLent: + t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 + if n[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: + n[0] = n[0][1] + n[0] = takeImplicitAddr(c, n[0], t.kind == tyLent) of tyTuple: - for i in 0.. <t.sonsLen: - var e = skipTypes(t.sons[i], {tyGenericInst}) - if e.kind == tyVar: - if n.sons[0].kind == nkPar: - n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i]) - elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and - n.sons[0].sons[1].kind == nkPar: - var a = n.sons[0].sons[1] - a.sons[i] = takeImplicitAddr(c, a.sons[i]) + for i in 0..<t.len: + let e = skipTypes(t[i], {tyGenericInst, tyAlias, tySink}) + if e.kind in {tyVar, tyLent}: + e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 + let tupleConstr = if n[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: n[0][1] else: n[0] + if tupleConstr.kind in {nkPar, nkTupleConstr}: + if tupleConstr[i].kind == nkExprColonExpr: + tupleConstr[i][1] = takeImplicitAddr(c, tupleConstr[i][1], e.kind == tyLent) + else: + tupleConstr[i] = takeImplicitAddr(c, tupleConstr[i], e.kind == tyLent) else: - localError(n.sons[0].info, errXExpected, "tuple constructor") - else: nil - -proc SemYield(c: PContext, n: PNode): PNode = + 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) + checkSonsLen(n, 1, c.config) if c.p.owner == nil or c.p.owner.kind != skIterator: - LocalError(n.info, errYieldNotAllowedHere) - elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: - LocalError(n.info, errYieldNotAllowedInTryStmt) - elif n.sons[0].kind != nkEmpty: - n.sons[0] = SemExprWithType(c, n.sons[0]) # check for type compatibility: - var restype = c.p.owner.typ.sons[0] + localError(c.config, n.info, errYieldNotAllowedHere) + elif n[0].kind != nkEmpty: + var iterType = c.p.owner.typ + let restype = iterType[0] + n[0] = semExprWithType(c, n[0], {}, restype) # check for type compatibility: if restype != nil: - n.sons[0] = fitNode(c, restype, n.sons[0]) - if n.sons[0].typ == nil: InternalError(n.info, "semYield") - SemYieldVarResult(c, n, restype) + if n[0].typ == nil: internalError(c.config, n.info, "semYield") + + if resultTypeIsInferrable(restype): + let inferred = n[0].typ + iterType[0] = inferred + if c.p.resultSym != nil: + c.p.resultSym.typ = inferred + else: + n[0] = fitNode(c, restype, n[0], n.info) + + semYieldVarResult(c, n, restype) else: - localError(n.info, errCannotReturnExpr) - elif c.p.owner.typ.sons[0] != nil: - localError(n.info, errGenerated, "yield statement must yield a value") + localError(c.config, n.info, errCannotReturnExpr) + elif c.p.owner.typ.returnType != nil: + localError(c.config, n.info, errGenerated, "yield statement must yield a value") + +proc considerQuotedIdentOrDot(c: PContext, n: PNode, origin: PNode = nil): PIdent = + if n.kind == nkDotExpr: + let a = considerQuotedIdentOrDot(c, n[0], origin).s + let b = considerQuotedIdentOrDot(c, n[1], origin).s + var s = newStringOfCap(a.len + b.len + 1) + s.add(a) + s.add('.') + s.add(b) + result = getIdent(c.cache, s) + else: + result = considerQuotedIdent(c, n, origin) -proc lookUpForDefined(c: PContext, i: PIdent, onlyCurrentScope: bool): PSym = - if onlyCurrentScope: - result = localSearchInScope(c, i) - else: - result = searchInScopes(c, i) # no need for stub loading +proc 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) - var m = LookupForDefined(c, n.sons[0], onlyCurrentScope) - if (m != nil) and (m.kind == skModule): - if (n.sons[1].kind == nkIdent): - var ident = n.sons[1].ident - if m == c.module: - result = StrTableGet(c.topLevelScope.symbols, ident) - else: - result = StrTableGet(m.tab, ident) - else: - LocalError(n.sons[1].info, errIdentifierExpected, "") - of nkAccQuoted: - result = lookupForDefined(c, considerAcc(n), onlyCurrentScope) + if onlyCurrentScope: return + checkSonsLen(n, 2, c.config) + var m = lookUpForDeclared(c, n[0], onlyCurrentScope) + if m != nil and m.kind == skModule: + let ident = considerQuotedIdent(c, n[1], n) + if m == c.module: + result = strTableGet(c.topLevelScope.symbols, ident) + else: + result = someSym(c.graph, m, ident) of nkSym: result = n.sym - else: - LocalError(n.info, errIdentifierExpected, renderTree(n)) + of nkOpenSymChoice, nkClosedSymChoice: + result = n[0].sym + of nkOpenSym: + result = lookUpForDeclared(c, n[0], onlyCurrentScope) + else: + localError(c.config, n.info, "identifier expected, but got: " & renderTree(n)) result = nil -proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = - checkSonsLen(n, 2) +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 LookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil: - result.intVal = 1 - elif not onlyCurrentScope and (n.sons[1].kind == nkIdent) and - condsyms.isDefined(n.sons[1].ident): - result.intVal = 1 + result.intVal = ord lookUpForDeclared(c, n[1], onlyCurrentScope) != nil result.info = n.info - result.typ = getSysType(tyBool) - -proc setMs(n: PNode, s: PSym): PNode = - result = n - n.sons[0] = newSymNode(s) - n.sons[0].info = n.info + result.typ = getSysType(c.graph, n.info, tyBool) proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym = ## The argument to the proc should be nkCall(...) or similar ## Returns the macro/template symbol if isCallExpr(n): - var expandedSym = qualifiedLookup(c, n[0], {checkUndeclared}) + var expandedSym = qualifiedLookUp(c, n[0], {checkUndeclared}) if expandedSym == nil: - LocalError(n.info, errUndeclaredIdentifier, n[0].renderTree) + errorUndeclaredIdentifier(c, n.info, n[0].renderTree) return errorSym(c, n[0]) if expandedSym.kind notin {skMacro, skTemplate}: - LocalError(n.info, errXisNoMacroOrTemplate, expandedSym.name.s) + localError(c.config, n.info, "'$1' is not a macro or template" % expandedSym.name.s) return errorSym(c, n[0]) result = expandedSym else: - LocalError(n.info, errXisNoMacroOrTemplate, n.renderTree) + localError(c.config, n.info, "'$1' is not a macro or template" % n.renderTree) result = errorSym(c, n) proc expectString(c: PContext, n: PNode): string = @@ -1332,166 +2292,231 @@ proc expectString(c: PContext, n: PNode): string = if n.kind in nkStrKinds: return n.strVal else: - LocalError(n.info, errStringLiteralExpected) - -proc getMagicSym(magic: TMagic): PSym = - result = newSym(skProc, getIdent($magic), GetCurrOwner(), gCodegenLineInfo) - result.magic = magic - -proc newAnonSym(kind: TSymKind, info: TLineInfo, - owner = getCurrOwner()): PSym = - result = newSym(kind, idAnon, owner, info) - result.flags = {sfGenSym} - -proc semUsing(c: PContext, n: PNode): PNode = - result = newNodeI(nkEmpty, n.info) - for e in n.sons: - let usedSym = semExpr(c, e) - if usedSym.kind == nkSym: - case usedSym.sym.kind - of skLocalVars + {skConst}: - c.currentScope.usingSyms.safeAdd(usedSym) - continue - of skProcKinds: - addDeclAt(c.currentScope, usedSym.sym) - continue - else: nil - - LocalError(e.info, errUsingNoSymbol, e.renderTree) - -proc semExpandToAst(c: PContext, n: PNode): PNode = - var macroCall = n[1] - var expandedSym = expectMacroOrTemplateCall(c, macroCall) - if expandedSym.kind == skError: return n + result = "" + localError(c.config, n.info, errStringLiteralExpected) - macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) - markUsed(n, expandedSym) +proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym = + result = newSym(kind, c.cache.idAnon, c.idgen, getCurrOwner(c), info) - for i in countup(1, macroCall.len-1): - macroCall.sons[i] = semExprWithType(c, macroCall[i], {}) +proc semExpandToAst(c: PContext, n: PNode): PNode = + let macroCall = n[1] + + when false: + let expandedSym = expectMacroOrTemplateCall(c, macroCall) + if expandedSym.kind == skError: return n + + macroCall[0] = newSymNode(expandedSym, macroCall.info) + markUsed(c, n.info, expandedSym) + onUse(n.info, expandedSym) + + if isCallExpr(macroCall): + for i in 1..<macroCall.len: + #if macroCall[0].typ[i].kind != tyUntyped: + macroCall[i] = semExprWithType(c, macroCall[i], {}) + # performing overloading resolution here produces too serious regressions: + let headSymbol = macroCall[0] + var cands = 0 + var cand: PSym = nil + var o: TOverloadIter = default(TOverloadIter) + var symx = initOverloadIter(o, c, headSymbol) + while symx != nil: + if symx.kind in {skTemplate, skMacro} and symx.typ.len == macroCall.len: + cand = symx + inc cands + symx = nextOverloadIter(o, c, headSymbol) + if cands == 0: + localError(c.config, n.info, "expected a template that takes " & $(macroCall.len-1) & " arguments") + elif cands >= 2: + localError(c.config, n.info, "ambiguous symbol in 'getAst' context: " & $macroCall) + else: + let info = macroCall[0].info + macroCall[0] = newSymNode(cand, info) + markUsed(c, info, cand) + onUse(info, cand) + # we just perform overloading resolution here: + #n[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro}) + else: + localError(c.config, n.info, "getAst takes a call, but got " & n.renderTree) # Preserve the magic symbol in order to be handled in evals.nim - InternalAssert n.sons[0].sym.magic == mExpandToAst - n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType - result = n + internalAssert c.config, n[0].sym.magic == mExpandToAst + #n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType + if n.kind == nkStmtList and n.len == 1: result = n[0] + else: result = n + result.typ = sysTypeFromName(c.graph, n.info, "NimNode") proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, flags: TExprFlags = {}): PNode = - if 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) -proc processQuotations(n: var PNode, op: string, +proc processQuotations(c: PContext; n: var PNode, op: string, quotes: var seq[PNode], ids: var seq[PNode]) = template returnQuote(q) = quotes.add q - n = newIdentNode(getIdent($quotes.len), n.info) + n = newIdentNode(getIdent(c.cache, $quotes.len), n.info) ids.add n return - if n.kind == nkPrefix: - checkSonsLen(n, 2) - if n[0].kind == nkIdent: - var examinedOp = n[0].ident.s + 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(examinedOp.substr(op.len)), n.info) - elif n.kind == nkAccQuoted and op == "``": - returnQuote n[0] - - if not n.isAtom: - for i in 0 .. <n.len: - processQuotations(n.sons[i], op, quotes, ids) + prefixed[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), prefixed.info) + + if n.kind == nkPrefix: + checkSonsLen(n, 2, c.config) + handlePrefixOp(n) + elif n.kind == nkAccQuoted: + if op == "``": + returnQuote n[0] + else: # [bug #7589](https://github.com/nim-lang/Nim/issues/7589) + if n.len == 2 and n[0].ident.s == op: + var tempNode = nkPrefix.newTree() + tempNode.newSons(2) + tempNode[0] = n[0] + tempNode[1] = n[1] + handlePrefixOp(tempNode) + elif n.kind == nkIdent: + if n.ident.s == "result": + n = ids[0] + + for i in 0..<n.safeLen: + processQuotations(c, n[i], op, quotes, ids) proc semQuoteAst(c: PContext, n: PNode): PNode = - InternalAssert 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 - doBlk = n{-1} + quotedBlock = n[^1] op = if n.len == 3: expectString(c, n[1]) else: "``" - quotes = newSeq[PNode](1) - # the quotes will be added to a nkCall statement - # leave some room for the callee symbol - ids = newSeq[PNode]() + quotes = newSeq[PNode](2) + # the quotes will be added to a nkCall statement + # leave some room for the callee symbol and the result symbol + ids = newSeq[PNode](1) # this will store the generated param names + # leave some room for the result symbol + + if quotedBlock.kind != nkStmtList: + localError(c.config, n.info, errXExpected, "block") - if doBlk.kind != nkDo: - LocalError(n.info, errXExpected, "block") + # This adds a default first field to pass the result symbol + ids[0] = newAnonSym(c, skParam, n.info).newSymNode + processQuotations(c, quotedBlock, op, quotes, ids) + + let dummyTemplateSym = newAnonSym(c, skTemplate, n.info) + incl(dummyTemplateSym.flags, sfTemplateRedefinition) + var dummyTemplate = newProcNode( + nkTemplateDef, quotedBlock.info, body = quotedBlock, + params = c.graph.emptyNode, + name = dummyTemplateSym.newSymNode, + pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode, + pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode) - processQuotations(doBlk.sons[bodyPos], op, quotes, ids) - - doBlk.sons[namePos] = newAnonSym(skTemplate, n.info).newSymNode if ids.len > 0: - doBlk[paramsPos].sons.setLen(2) - doBlk[paramsPos].sons[0] = getSysSym("stmt").newSymNode # return type - ids.add getSysSym("expr").newSymNode # params type - ids.add emptyNode # no default value - doBlk[paramsPos].sons[1] = newNode(nkIdentDefs, n.info, ids) - - var tmpl = semTemplateDef(c, doBlk) + 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, @[ - getMagicSym(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 = {}, bufferErrors = false): PNode = +proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # watch out, hacks ahead: - let oldErrorCount = msgs.gErrorCounter - let oldErrorMax = msgs.gErrorMax - inc c.InCompilesContext - # do not halt after first error: - msgs.gErrorMax = high(int) - + when defined(nimsuggest): + # Remove the error hook so nimsuggest doesn't report errors there + let tempHook = c.graph.config.structuredErrorHook + c.graph.config.structuredErrorHook = nil + let oldErrorCount = c.config.errorCounter + let oldErrorMax = c.config.errorMax + let oldCompilesId = c.compilesContextId + # if this is a nested 'when compiles', do not increase the ID so that + # generic instantiations can still be cached for this level. + if c.compilesContextId == 0: + inc c.compilesContextIdGenerator + c.compilesContextId = c.compilesContextIdGenerator + c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here + # open a scope for temporary symbol inclusions: let oldScope = c.currentScope openScope(c) - let oldOwnerLen = len(gOwners) + let oldOwnerLen = c.graph.owners.len let oldGenerics = c.generics - let oldErrorOutputs = errorOutputs - errorOutputs = if bufferErrors: {eInMemory} else: {} - let oldContextLen = msgs.getInfoContextLen() - - let oldInGenericContext = c.InGenericContext - let oldInUnrolledContext = c.InUnrolledContext - let oldInGenericInst = c.InGenericInst + let oldErrorOutputs = c.config.m.errorOutputs + if efExplain notin flags: c.config.m.errorOutputs = {} + let oldContextLen = msgs.getInfoContextLen(c.config) + + let oldInGenericContext = c.inGenericContext + let oldInUnrolledContext = c.inUnrolledContext + let oldInGenericInst = c.inGenericInst + let oldInStaticContext = c.inStaticContext let oldProcCon = c.p c.generics = @[] + var err: string try: result = semExpr(c, n, flags) - if msgs.gErrorCounter != 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: - nil + result = nil # undo symbol table changes (as far as it's possible): + c.compilesContextId = oldCompilesId c.generics = oldGenerics - c.InGenericContext = oldInGenericContext - c.InUnrolledContext = oldInUnrolledContext - c.InGenericInst = oldInGenericInst + c.inGenericContext = oldInGenericContext + c.inUnrolledContext = oldInUnrolledContext + c.inGenericInst = oldInGenericInst + c.inStaticContext = oldInStaticContext c.p = oldProcCon - msgs.setInfoContextLen(oldContextLen) - setlen(gOwners, oldOwnerLen) + msgs.setInfoContextLen(c.config, oldContextLen) + setLen(c.graph.owners, oldOwnerLen) c.currentScope = oldScope - dec c.InCompilesContext - errorOutputs = oldErrorOutputs - msgs.gErrorCounter = oldErrorCount - msgs.gErrorMax = oldErrorMax + 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(tyBool) + 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) @@ -1501,536 +2526,1088 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = else: result = semDirectOp(c, n, flags) -proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = +proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType = + result = newType(tyGenericInvocation, c.idgen, c.module) + addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ, c.idgen) + addSonSkipIntLit(result, t, c.idgen) + result = instGenericContainer(c, info, result, allowMetaTypes = false) + +proc instantiateCreateFlowVarCall(c: PContext; t: PType; + info: TLineInfo): PSym = + let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar") + if sym == nil: + localError(c.config, info, "system needs: nimCreateFlowVar") + var bindings = 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.excl {sfCompilerProc, sfExportc, sfImportc} + result.loc.snippet = "" + +proc setMs(n: PNode, s: PSym): PNode = + result = n + n[0] = newSymNode(s) + n[0].info = n.info + +proc semSizeof(c: PContext, n: PNode): PNode = + if n.len != 2: + localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof") + else: + n[1] = semExprWithType(c, n[1], {efDetermineType}) + #restoreOldStyleType(n[1]) + n.typ = getSysType(c.graph, n.info, tyInt) + result = foldSizeOf(c.config, n, n) + +proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode = # this is a hotspot in the compiler! - # DON'T forget to update ast.SpecialSemMagics if you add a magic here! result = n case s.magic # magics that need special treatment - 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)) - of mOf: result = semOf(c, setMs(n, s)) - of mEcho: result = semEcho(c, setMs(n, s)) - of mShallowCopy: result = semShallowCopy(c, n, flags) - of mExpandToAst: result = semExpandToAst(c, n, s, flags) - of mQuoteAst: result = semQuoteAst(c, n) + of mAddr: + markUsed(c, n.info, s) + checkSonsLen(n, 2, c.config) + result = semAddr(c, n[1]) + of mTypeOf: + markUsed(c, n.info, s) + result = semTypeOf(c, n) + of mDefined: + markUsed(c, n.info, s) + result = semDefined(c, setMs(n, s)) + of mDeclared: + markUsed(c, n.info, s) + result = semDeclared(c, setMs(n, s), false) + of mDeclaredInScope: + markUsed(c, n.info, s) + result = semDeclared(c, setMs(n, s), true) + of mCompiles: + markUsed(c, n.info, s) + result = semCompiles(c, setMs(n, s), flags) + of mIs: + markUsed(c, n.info, s) + result = semIs(c, setMs(n, s), flags) + of mShallowCopy: + markUsed(c, n.info, s) + result = semShallowCopy(c, n, flags) + of mExpandToAst: + markUsed(c, n.info, s) + result = semExpandToAst(c, n, s, flags) + of mQuoteAst: + markUsed(c, n.info, s) + result = semQuoteAst(c, n) of mAstToStr: - checkSonsLen(n, 2) - result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) - result.typ = getSysType(tyString) - else: result = semDirectOp(c, n, flags) + markUsed(c, n.info, s) + checkSonsLen(n, 2, c.config) + result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph) + result.typ = getSysType(c.graph, n.info, tyString) + of mParallel: + markUsed(c, n.info, s) + if parallel notin c.features: + localError(c.config, n.info, "use the {.experimental.} pragma to enable 'parallel'") + result = setMs(n, s) + var x = n.lastSon + if x.kind == nkDo: x = x[bodyPos] + inc c.inParallelStmt + result[1] = semStmt(c, x, {}) + dec c.inParallelStmt + of mSpawn: + markUsed(c, n.info, s) + when defined(leanCompiler): + result = localErrorNode(c, n, "compiler was built without 'spawn' support") + else: + result = setMs(n, s) + for i in 1..<n.len: + result[i] = semExpr(c, n[i]) + + if n.len > 1 and n[1].kind notin nkCallKinds: + return localErrorNode(c, n, n[1].info, "'spawn' takes a call expression; got: " & $n[1]) + + let typ = result[^1].typ + if not typ.isEmptyType: + if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar: + result.typ = createFlowVar(c, typ, n.info) + else: + result.typ = typ + result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode + else: + result.add c.graph.emptyNode + of mProcCall: + markUsed(c, n.info, s) + result = setMs(n, s) + result[1] = semExpr(c, n[1]) + result.typ = n[1].typ + of mPlugin: + markUsed(c, n.info, s) + # semDirectOp with conditional 'afterCallActions': + let nOrig = n.copyTree + #semLazyOpAux(c, n) + result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) + if result == nil: + result = errorNode(c, n) + else: + let callee = result[0].sym + if callee.magic == mNone: + semFinishOperands(c, result) + activate(c, result) + fixAbstractType(c, result) + analyseIfAddressTakenInCall(c, result) + if callee.magic != mNone: + result = magicsAfterOverloadResolution(c, result, flags) + of mRunnableExamples: + markUsed(c, n.info, s) + if c.config.cmd in cmdDocLike and n.len >= 2 and n.lastSon.kind == nkStmtList: + when false: + # some of this dead code was moved to `prepareExamples` + if sfMainModule in c.module.flags: + let inp = toFullPath(c.config, c.module.info) + if c.runnableExamples == nil: + c.runnableExamples = newTree(nkStmtList, + newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) + let imports = newTree(nkStmtList) + var savedLastSon = copyTree n.lastSon + extractImports(savedLastSon, imports) + for imp in imports: c.runnableExamples.add imp + c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree savedLastSon) + result = setMs(n, s) + else: + result = c.graph.emptyNode + of mSizeOf: + markUsed(c, n.info, s) + result = semSizeof(c, setMs(n, s)) + of mArrToSeq, mOpenArrayToSeq: + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind in {tySequence, tyOpenArray}): + # seq type inference + var arrayType = newType(tyOpenArray, c.idgen, expected.owner) + arrayType.rawAddSon(expected[0]) + if n[0].kind == nkSym and sfFromGeneric in n[0].sym.flags: + # may have been resolved to `@`[empty] at some point, + # reset to `@` to deal with this + n[0] = newSymNode(n[0].sym.instantiatedFrom, n[0].info) + n[1] = semExpr(c, n[1], flags, arrayType) + result = semDirectOp(c, n, flags, expectedType) + else: + result = semDirectOp(c, n, flags, expectedType) proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = # If semCheck is set to false, ``when`` will return the verbatim AST of # the correct branch. Otherwise the AST will be passed through semStmt. result = nil - - template setResult(e: expr) = - if semCheck: result = semStmt(c, e) # do not open a new scope! + let flags = if semCheck: {efWantStmt} else: {} + + template setResult(e: untyped) = + if semCheck: result = semExpr(c, e, flags) # do not open a new scope! else: result = e - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] + # Check if the node is "when nimvm" + # when nimvm: + # ... + # else: + # ... + var whenNimvm = false + var typ = commonTypeBegin + if n.len in 1..2 and n[0].kind == nkElifBranch and ( + n.len == 1 or n[1].kind == nkElse): + var exprNode = n[0][0] + if exprNode.kind == nkOpenSym: + exprNode = exprNode[0] + if exprNode.kind == nkIdent: + whenNimvm = lookUp(c, exprNode).magic == mNimvm + elif exprNode.kind == nkSym: + whenNimvm = exprNode.sym.magic == mNimvm + if whenNimvm: n.flags.incl nfLL + + var cannotResolve = false + for i in 0..<n.len: + var it = n[i] case it.kind - of nkElifBranch, nkElifExpr: - checkSonsLen(it, 2) - var e = semConstExpr(c, it.sons[0]) - if e.kind != nkIntLit: - # can happen for cascading errors, assume false - # InternalError(n.info, "semWhen") - discard - elif e.intVal != 0 and result == nil: - setResult(it.sons[1]) + of nkElifBranch, nkElifExpr: + checkSonsLen(it, 2, c.config) + if whenNimvm: + if semCheck: + it[1] = semExpr(c, it[1], flags) + typ = commonType(c, typ, it[1].typ) + result = n # when nimvm is not elimited until codegen + elif c.inGenericContext > 0: + let e = semExprWithType(c, it[0]) + if e.typ.kind == tyFromExpr: + it[0] = makeStaticExpr(c, e) + cannotResolve = true + else: + it[0] = forceBool(c, e) + let val = getConstExpr(c.module, it[0], c.idgen, c.graph) + if val == nil or val.kind != nkIntLit: + cannotResolve = true + elif not cannotResolve and val.intVal != 0 and result == nil: + setResult(it[1]) + return # we're not in nimvm and we already have a result + else: + let e = forceBool(c, semConstExpr(c, it[0])) + if e.kind != nkIntLit: + # can happen for cascading errors, assume false + # InternalError(n.info, "semWhen") + discard + elif e.intVal != 0 and result == nil: + setResult(it[1]) + return # we're not in nimvm and we already have a result of nkElse, nkElseExpr: - checkSonsLen(it, 1) - if result == nil: - setResult(it.sons[0]) - else: illFormedAst(n) + checkSonsLen(it, 1, c.config) + if cannotResolve: + discard + elif result == nil or whenNimvm: + if semCheck: + it[0] = semExpr(c, it[0], flags) + typ = commonType(c, typ, it[0].typ) + if typ != nil and typ.kind != tyUntyped: + it[0] = fitNode(c, typ, it[0], it[0].info) + if result == nil: + result = it[0] + else: illFormedAst(n, c.config) + if cannotResolve: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return if result == nil: - result = newNodeI(nkEmpty, n.info) - # 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) + result = newNodeI(nkEmpty, n.info) + if whenNimvm: + result.typ = typ + if n.len == 1: + result.add(newTree(nkElse, newNode(nkStmtList))) -proc semSetConstr(c: PContext, n: PNode): 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)) - else: + 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) - n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1]) - n.sons[i].sons[2] = semExprWithType(c, n.sons[i].sons[2]) - if typ == nil: - typ = skipTypes(n.sons[i].sons[1].typ, - {tyGenericInst, tyVar, tyOrdinal}) - n.sons[i].typ = n.sons[i].sons[2].typ # range node needs type too - elif n.sons[i].kind == nkRange: + for i in 0..<n.len: + let doSetType = typ == nil + if isRange(n[i]): + checkSonsLen(n[i], 3, c.config) + n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType) + n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType) + if doSetType: + typ = skipTypes(n[i][1].typ, + {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) + n[i].typ = n[i][2].typ # range node needs type too + elif n[i].kind == nkRange: # already semchecked - if typ == nil: - typ = skipTypes(n.sons[i].sons[0].typ, - {tyGenericInst, tyVar, tyOrdinal}) + 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, tyOrdinal}) - if not isOrdinalType(typ): - LocalError(n.info, errOrdinalTypeExpected) - typ = makeRangeType(c, 0, MaxSetElements - 1, n.info) - elif lengthOrd(typ) > MaxSetElements: - typ = makeRangeType(c, 0, MaxSetElements - 1, n.info) - addSonSkipIntLit(result.typ, typ) - for i in countup(0, sonsLen(n) - 1): + 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 - if isRange(n.sons[i]): - m = newNodeI(nkRange, n.sons[i].info) - addSon(m, fitNode(c, typ, n.sons[i].sons[1])) - addSon(m, fitNode(c, typ, n.sons[i].sons[2])) - elif n.sons[i].kind == nkRange: m = n.sons[i] # already semchecked + let info = n[i].info + if isRange(n[i]): + m = newNodeI(nkRange, info) + m.add fitNode(c, typ, n[i][1], info) + m.add fitNode(c, typ, n[i][2], info) + elif n[i].kind == nkRange: m = n[i] # already semchecked else: - m = fitNode(c, typ, n.sons[i]) - addSon(result, m) + m = fitNode(c, typ, n[i], info) + result.add m -proc semTableConstr(c: PContext, n: PNode): PNode = - # we simply transform ``{key: value, key2, key3: value}`` to +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): - var pair = newNodeI(nkPar, x.info) - pair.add(n.sons[j]) + for i in 0..<n.len: + var x = n[i] + if x.kind == nkExprColonExpr and x.len == 2: + for j in lastKey..<i: + var pair = newNodeI(nkTupleConstr, x.info) + pair.add(n[j]) pair.add(x[1]) result.add(pair) - var pair = newNodeI(nkPar, x.info) + var pair = newNodeI(nkTupleConstr, x.info) pair.add(x[0]) pair.add(x[1]) result.add(pair) lastKey = i+1 - if lastKey != n.len: illFormedAst(n) - result = semExpr(c, result) + if lastKey != n.len: illFormedAst(n, c.config) + result = semExpr(c, result, expectedType = expectedType) -type - TParKind = enum +type + TParKind = enum paNone, paSingle, paTupleFields, paTuplePositions -proc checkPar(n: PNode): TParKind = - var length = sonsLen(n) - if length == 0: +proc checkPar(c: PContext; n: PNode): TParKind = + if n.len == 0: result = paTuplePositions # () - elif length == 1: - result = paSingle # (expr) - else: - 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[0].kind == nkExprColonExpr: result = paTupleFields else: result = paTuplePositions - for i in countup(0, length - 1): - if result == paTupleFields: - if (n.sons[i].kind != nkExprColonExpr) or - not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): - LocalError(n.sons[i].info, errNamedExprExpected) + for i in 0..<n.len: + if result == paTupleFields: + if (n[i].kind != nkExprColonExpr) or + n[i][0].kind notin {nkSym, nkIdent, nkAccQuoted}: + localError(c.config, n[i].info, errNamedExprExpected) return paNone - else: - if n.sons[i].kind == nkExprColonExpr: - LocalError(n.sons[i].info, errNamedExprNotAllowed) + else: + if n[i].kind == nkExprColonExpr: + localError(c.config, n[i].info, errNamedExprNotAllowed) return paNone -proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = - result = newNodeI(nkPar, n.info) +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.sons[i].kind != nkExprColonExpr) or - not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): - illFormedAst(n.sons[i]) - var id: PIdent - if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident - else: id = n.sons[i].sons[0].sym.name - if ContainsOrIncl(ids, id.id): - localError(n.sons[i].info, errFieldInitTwice, id.s) - n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1], - flags*{efAllowDestructor}) - var f = newSymS(skField, n.sons[i].sons[0], c) - f.typ = skipIntLit(n.sons[i].sons[1].typ) + for i in 0..<n.len: + if n[i].kind != nkExprColonExpr: + illFormedAst(n[i], c.config) + let id = considerQuotedIdent(c, n[i][0]) + if containsOrIncl(ids, id.id): + localError(c.config, n[i].info, errFieldInitTwice % id.s) + # can check if field name matches expected type here + let expectedElemType = if expected != nil: expected[i] else: nil + n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType) + if expectedElemType != nil and + (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)): + # hasEmpty/nil check is to not break existing code like + # `const foo = [(1, {}), (2, {false})]`, + # `const foo = if true: (0, nil) else: (1, new(int))` + n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info) + + if n[i][1].typ.kind == tyTypeDesc: + localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.") + n[i][1].typ = errorType(c) + + var f = newSymS(skField, n[i][0], c) + f.typ = skipIntLit(n[i][1].typ.skipTypes({tySink}), c.idgen) f.position = i rawAddSon(typ, f.typ) - 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.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 checkInitialized(n: PNode, ids: TIntSet, info: TLineInfo) = - case n.kind - of nkRecList: - for i in countup(0, sonsLen(n) - 1): - checkInitialized(n.sons[i], ids, info) - of nkRecCase: - if (n.sons[0].kind != nkSym): InternalError(info, "checkInitialized") - checkInitialized(n.sons[0], ids, info) - when false: - # XXX we cannot check here, as we don't know the branch! - for i in countup(1, sonsLen(n) - 1): - case n.sons[i].kind - of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info) - else: internalError(info, "checkInitialized") - of nkSym: - if tfNeedsInit in n.sym.typ.flags and n.sym.name.id notin ids: - Message(info, errGenerated, "field not initialized: " & n.sym.name.s) - else: internalError(info, "checkInitialized") - -proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = - var t = semTypeNode(c, n.sons[0], nil) - result = n - result.typ = t - result.kind = nkObjConstr - t = skipTypes(t, abstractInst) - if t.kind == tyRef: t = skipTypes(t.sons[0], abstractInst) - if t.kind != tyObject: - localError(n.info, errGenerated, "object constructor needs an object type") - return - var objType = t - var ids = initIntSet() - for i in 1.. <n.len: - let it = n.sons[i] - if it.kind != nkExprColonExpr or it.sons[0].kind notin {nkSym, nkIdent}: - localError(n.info, errNamedExprExpected) - break - var id: PIdent - if it.sons[0].kind == nkIdent: id = it.sons[0].ident - else: id = it.sons[0].sym.name - if ContainsOrIncl(ids, id.id): - localError(it.info, errFieldInitTwice, id.s) - var e = semExprWithType(c, it.sons[1], flags*{efAllowDestructor}) - var - check: PNode = nil - f: PSym - t = objType - while true: - check = nil - f = lookupInRecordAndBuildCheck(c, it, t.n, id, check) - if f != nil: break - if t.sons[0] == nil: break - t = skipTypes(t.sons[0], {tyGenericInst}) - if f != nil and fieldVisible(c, f): - it.sons[0] = newSymNode(f) - e = fitNode(c, f.typ, e) - # small hack here in a nkObjConstr the ``nkExprColonExpr`` node can have - # 3 childen the last being the field check - if check != nil: - check.sons[0] = it.sons[0] - it.add(check) - else: - localError(it.info, errUndeclaredFieldX, id.s) - it.sons[1] = e - # XXX object field name check for 'case objects' if the kind is static? - if tfNeedsInit in objType.flags: - while true: - checkInitialized(objType.n, ids, n.info) - if objType.sons[0] == nil: break - objType = skipTypes(objType.sons[0], {tyGenericInst}) +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) - checkSonsLen(n, 2) + 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(n.sons[0], 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) - Dec(c.p.nestedBlockCounter) - -proc buildCall(n: PNode): PNode = - if n.kind == nkDotExpr and n.len == 2: - # x.y --> y(x) - result = newNodeI(nkCall, n.info, 2) - result.sons[0] = n.sons[1] - result.sons[1] = n.sons[0] - elif n.kind in nkCallKinds and n.sons[0].kind == nkDotExpr: - # x.y(a) -> y(x, a) - let a = n.sons[0] - result = newNodeI(nkCall, n.info, n.len+1) - result.sons[0] = a.sons[1] - result.sons[1] = a.sons[0] - for i in 1 .. <n.len: result.sons[i+1] = n.sons[i] - else: - result = n + c.p.breakInLoop = oldBreakInLoop + dec(c.p.nestedBlockCounter) -proc doBlockIsStmtList(n: PNode): bool = - result = n.kind == nkDo and - n[paramsPos].sonsLen == 1 and - n[paramsPos][0].kind == nkEmpty - -proc fixImmediateParams(n: PNode): PNode = - # XXX: Temporary work-around until we carry out - # the planned overload resolution reforms - for i in 1 .. <safeLen(n): - if doBlockIsStmtList(n[i]): - n.sons[i] = n[i][bodyPos] - result = n +proc semExportExcept(c: PContext, n: PNode): PNode = + let moduleName = semExpr(c, n[0]) + if moduleName.kind != nkSym or moduleName.sym.kind != skModule: + localError(c.config, n.info, "The export/except syntax expects a module name") + return n + let exceptSet = readExceptSet(c, n) + let exported = moduleName.sym + result = newNodeI(nkExportStmt, n.info) + reexportSym(c, exported) + for s in allSyms(c.graph, exported): + if s.kind in ExportableSymKinds+{skModule} and + s.name.id notin exceptSet and sfError notin s.flags: + reexportSym(c, s) + result.add newSymNode(s, n.info) + markUsed(c, n.info, exported) proc semExport(c: PContext, n: PNode): PNode = - var x = newNodeI(n.kind, n.info) - #let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len - for i in 0.. <n.len: - let a = n.sons[i] - var o: TOverloadIter + proc specialSyms(c: PContext; s: PSym) {.inline.} = + if s.kind == skConverter: addConverter(c, LazySym(sym: s)) + elif s.kind == skType and s.typ != nil and s.typ.kind == tyEnum and sfPure in s.flags: + addPureEnum(c, LazySym(sym: s)) + + result = newNodeI(nkExportStmt, n.info) + for i in 0..<n.len: + let a = n[i] + var o: TOverloadIter = default(TOverloadIter) var s = initOverloadIter(o, c, a) if s == nil: - localError(a.info, errGenerated, "invalid expr for 'export': " & - renderTree(a)) - while s != nil: - if s.kind in ExportableSymKinds+{skModule}: - x.add(newSymNode(s, a.info)) - s = nextOverloadIter(o, c, a) - if c.module.ast.isNil: - c.module.ast = newNodeI(nkStmtList, n.info) - assert c.module.ast.kind == nkStmtList - c.module.ast.add x - result = n + localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a)) + elif s.kind == skModule: + # forward everything from that module: + reexportSym(c, s) + for it in allSyms(c.graph, s): + if it.kind in ExportableSymKinds+{skModule}: + reexportSym(c, it) + result.add newSymNode(it, a.info) + specialSyms(c, it) + markUsed(c, n.info, s) + else: + while s != nil: + if s.kind == skEnumField: + localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a) & + "; enum field cannot be exported individually") + if s.kind in ExportableSymKinds+{skModule} and sfError notin s.flags: + result.add(newSymNode(s, a.info)) + reexportSym(c, s) + markUsed(c, n.info, s) + specialSyms(c, s) + if s.kind == skType and sfPure notin s.flags: + var etyp = s.typ + if etyp.kind in {tyBool, tyEnum}: + for j in 0..<etyp.n.len: + var e = etyp.n[j].sym + if e.kind != skEnumField: + internalError(c.config, s.info, "rawImportSymbol") + reexportSym(c, e) + + s = nextOverloadIter(o, c, a) + +proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = + var tupexp = semTuplePositionsConstr(c, n, flags, expectedType) + var isTupleType: bool = false + if tupexp.len > 0: # don't interpret () as type + isTupleType = tupexp[0].typ.kind == tyTypeDesc + # check if either everything or nothing is tyTypeDesc + for i in 1..<tupexp.len: + if isTupleType != (tupexp[i].typ.kind == tyTypeDesc): + return localErrorNode(c, n, tupexp[i].info, "Mixing types and values in tuples is not allowed.") + if isTupleType: # expressions as ``(int, string)`` are reinterpret as type expressions + result = n + var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) + result.typ = makeTypeDesc(c, typ) + else: + result = tupexp + +proc isExplicitGenericCall(c: PContext, n: PNode): bool = + ## checks if a call node `n` is a routine call with explicit generic params + ## + ## the callee node needs to be either an nkBracketExpr or a call to a + ## symchoice of `[]` in which case it will be transformed into nkBracketExpr + ## + ## the LHS of the bracket expr has to either be a symchoice or resolve to + ## a routine symbol + template checkCallee(n: PNode) = + # check subscript LHS, `n` must be mutable + if isSymChoice(n): + result = true + else: + let s = qualifiedLookUp(c, n, {}) + if s != nil and s.kind in routineKinds: + result = true + n = semSymGenericInstantiation(c, n, s) + assert n.kind in nkCallKinds + result = false + let a = n[0] + case a.kind + of nkBracketExpr: + checkCallee(a[0]) + of nkCallKinds: + let b = a[0] + if b.kind in nkSymChoices: + let name = b.getPIdent + if name != nil and name.s == "[]": + checkCallee(a[1]) + if result: + # transform callee into normal bracket expr, only on success + let be = newNodeI(nkBracketExpr, a.info) + for i in 1..<a.len: be.add(a[i]) + n[0] = be + else: + result = false + +proc asBracketExpr(c: PContext; n: PNode): PNode = + proc isGeneric(c: PContext; n: PNode): bool = + if n.kind in {nkIdent, nkAccQuoted}: + let s = qualifiedLookUp(c, n, {}) + result = s != nil and isGenericRoutineStrict(s) + else: + result = false + + assert n.kind in nkCallKinds + if n.len > 1 and isGeneric(c, n[1]): + let b = n[0] + if b.kind in nkSymChoices: + for i in 0..<b.len: + if b[i].kind == nkSym and b[i].sym.magic == mArrGet: + result = newNodeI(nkBracketExpr, n.info) + for i in 1..<n.len: result.add(n[i]) + return result + return nil + +proc isOpenArraySym(x: PNode): bool = + var x = x + while true: + case x.kind + of {nkAddr, nkHiddenAddr}: + x = x[0] + of {nkHiddenStdConv, nkHiddenDeref}: + x = x[1] + else: + break + result = x.kind == nkSym + +proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) = + # This takes care of complicated signatures such as: + # proc foo(a: int, b = a) + # proc bar(a: int, b: int, c = a + b) + # + # The recursion may confuse you. It performs two duties: + # + # 1) extracting all referenced params from default expressions + # into a let section preceding the call + # + # 2) replacing the "references" within the default expression + # with these extracted skLet symbols. + # + # The first duty is carried out directly in the code here, while the second + # duty is activated by returning a non-nil value. The caller is responsible + # for replacing the input to the function with the returned non-nil value. + # (which is the hoisted symbol) + if defExpr.kind == nkSym and defExpr.sym.kind == skParam and + (defExpr.sym.owner == call[0].sym or + # symbol was resolved before proc was instantiated: + (sfFromGeneric in call[0].sym.flags and + defExpr.sym.owner == call[0].sym.instantiatedFrom)): + let paramPos = defExpr.sym.position + 1 + + if call[paramPos].skipAddr.kind != nkSym and not ( + skipTypes(call[paramPos].typ, abstractVar).kind in {tyOpenArray, tyVarargs} and + isOpenArraySym(call[paramPos]) + ): + let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen, + c.p.owner, letSection.info, c.p.owner.options) + hoistedVarSym.typ = call[paramPos].typ + + letSection.add newTreeI(nkIdentDefs, letSection.info, + newSymNode(hoistedVarSym), + newNodeI(nkEmpty, letSection.info), + call[paramPos]) + + call[paramPos] = newSymNode(hoistedVarSym) # Refer the original arg to its hoisted sym + + # arg we refer to is a sym, whether introduced by hoisting or not doesn't matter, we simply reuse it + defExpr = call[paramPos] + else: + for i in 0..<defExpr.safeLen: + hoistParamsUsedInDefault(c, call, letSection, defExpr[i]) + +proc getNilType(c: PContext): PType = + result = c.nilTypeCache + if result == nil: + result = newTypeS(tyNil, c) + result.size = c.config.target.ptrSize + result.align = c.config.target.ptrSize.int16 + c.nilTypeCache = result + +proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode = + var o: TOverloadIter = default(TOverloadIter) + var i = 0 + var a = initOverloadIter(o, c, n) + while a != nil: + if a.kind == skEnumField: + inc(i) + if i > 1: break + a = nextOverloadIter(o, c, n) + let info = getCallLineInfo(n) + if i <= 1: + if sfGenSym notin s.flags: + result = newSymNode(s, info) + markUsed(c, info, s, efInCall notin flags) + onUse(info, s) + else: + result = n + else: + result = newNodeIT(nkClosedSymChoice, info, newTypeS(tyNone, c)) + a = initOverloadIter(o, c, n) + while a != nil: + if a.kind == skEnumField: + incl(a.flags, sfUsed) + markOwnerModuleAsUsed(c, a) + result.add newSymNode(a, info) + onUse(info, a) + a = nextOverloadIter(o, c, n) + +proc semPragmaStmt(c: PContext; n: PNode) = + if c.p.owner.kind == skModule: + pragma(c, c.p.owner, n, stmtPragmas+stmtPragmasTopLevel, true) + else: + pragma(c, c.p.owner, n, stmtPragmas, true) + +proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode, + flags: TExprFlags, expectedType: PType): PSym = + # result is nil on error or if a node that can't produce a sym is resolved + let ident = considerQuotedIdent(c, n) + var filter = {low(TSymKind)..high(TSymKind)} + if efNoEvaluateGeneric in flags or expectedType != nil: + # `a[...]` where `a` is a module or package is not possible + filter.excl {skModule, skPackage} + let includePureEnum = expectedType != nil and + expectedType.skipTypes(abstractRange-{tyDistinct}).kind == tyEnum + let candidates = lookUpCandidates(c, ident, filter, + includePureEnum = includePureEnum) + if candidates.len == 0: + result = errorUndeclaredIdentifierHint(c, ident, n.info) + elif candidates.len == 1 or {efNoEvaluateGeneric, efInCall} * flags != {}: + # unambiguous, or we don't care about ambiguity + result = candidates[0] + else: + # ambiguous symbols have 1 last chance as a symchoice + var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c)) + for cand in candidates: + case cand.kind + of skModule, skPackage: + discard + of skType: + choice.add newSymNodeTypeDesc(cand, c.idgen, n.info) + else: + choice.add newSymNode(cand, n.info) + if choice.len == 0: + # we know candidates.len > 1, we just couldn't put any in a symchoice + errorUseQualifier(c, n.info, candidates) + return nil + resolveSymChoice(c, choice, flags, expectedType) + # choice.len == 1 can be true here but as long as it's a symchoice + # it's still not resolved + if isSymChoice(choice): + result = nil + if efAllowSymChoice in flags: + resultNode = choice + else: + errorUseQualifier(c, n.info, candidates) + else: + if choice.kind == nkSym: + result = choice.sym + else: + # resolution could have generated nkHiddenStdConv etc + resultNode = semExpr(c, choice, flags, expectedType) + result = nil + +proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = + when defined(nimCompilerStacktraceHints): + setFrameMsg c.config$n.info & " " & $n.kind + when false: # see `tdebugutils` + if isCompilerDebug(): + echo (">", c.config$n.info, n, flags, n.kind) + defer: + if isCompilerDebug(): + echo ("<", c.config$n.info, n, ?.result.typ) + template directLiteral(typeKind: TTypeKind) = + if result.typ == nil: + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind == typeKind): + result.typ = expected + changeType(c, result, expectedType, check=true) + else: + result.typ = getSysType(c.graph, n.info, typeKind) -proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = n - if gCmd == cmdIdeTools: suggestExpr(c, n) - if nfSem in n.flags: return + 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: - var s = lookUp(c, n) - semCaptureSym(s, c.p.owner) - result = semSym(c, n, s, flags) - if s.kind in {skProc, skMethod, skIterator, skConverter}: + let s = resolveIdentToSym(c, n, result, flags, expectedType) + if s == nil: + # resolveIdentToSym either errored or gave a result node + return + if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) + case s.kind + of skProc, skFunc, skMethod, skConverter, skIterator: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) if result.kind == nkSym: markIndirect(c, result.sym) - if isGenericRoutine(result.sym): - LocalError(n.info, errInstantiateXExplicitely, s.name.s) + # 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) - of nkEmpty, nkNone, nkCommentStmt: - nil - of nkNilLit: - result.typ = getSysType(tyNil) + result = semSym(c, n, s, flags) + of nkOpenSym: + assert n.len == 1 + let inner = n[0] + result = semOpenSym(c, inner, flags, expectedType) + of nkEmpty, nkNone, nkCommentStmt, nkType: + discard + of nkNilLit: + if result.typ == nil: + result.typ = getNilType(c) + if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}: + var m = newCandidate(c, result.typ) + if typeRel(m, expectedType, result.typ) >= isSubtype: + result.typ = expectedType + # or: result = fitNode(c, expectedType, result, n.info) of nkIntLit: - if result.typ == nil: setIntLitType(result) - of nkInt8Lit: - if result.typ == nil: result.typ = getSysType(tyInt8) - of nkInt16Lit: - if result.typ == nil: result.typ = getSysType(tyInt16) - of nkInt32Lit: - if result.typ == nil: result.typ = getSysType(tyInt32) - of nkInt64Lit: - if result.typ == nil: result.typ = getSysType(tyInt64) - of nkUIntLit: - if result.typ == nil: result.typ = getSysType(tyUInt) - of nkUInt8Lit: - if result.typ == nil: result.typ = getSysType(tyUInt8) - of nkUInt16Lit: - if result.typ == nil: result.typ = getSysType(tyUInt16) - of nkUInt32Lit: - if result.typ == nil: result.typ = getSysType(tyUInt32) - of nkUInt64Lit: - if result.typ == nil: result.typ = getSysType(tyUInt64) - of nkFloatLit: - if result.typ == nil: result.typ = getFloatLitType(result) - of nkFloat32Lit: - if result.typ == nil: result.typ = getSysType(tyFloat32) - of nkFloat64Lit: - if result.typ == nil: result.typ = getSysType(tyFloat64) - of nkFloat128Lit: - if result.typ == nil: result.typ = getSysType(tyFloat128) - of nkStrLit..nkTripleStrLit: - if result.typ == nil: result.typ = getSysType(tyString) - of nkCharLit: - if result.typ == nil: result.typ = getSysType(tyChar) - of nkDotExpr: + if result.typ == nil: + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind in {tyInt..tyInt64, + tyUInt..tyUInt64, + tyFloat..tyFloat128}): + if expected.kind in {tyFloat..tyFloat128}: + n.transitionIntToFloatKind(nkFloatLit) + changeType(c, result, expectedType, check=true) + else: + setIntLitType(c, result) + of nkInt8Lit: directLiteral(tyInt8) + of nkInt16Lit: directLiteral(tyInt16) + of nkInt32Lit: directLiteral(tyInt32) + of nkInt64Lit: directLiteral(tyInt64) + of nkUIntLit: directLiteral(tyUInt) + of nkUInt8Lit: directLiteral(tyUInt8) + of nkUInt16Lit: directLiteral(tyUInt16) + of nkUInt32Lit: directLiteral(tyUInt32) + of nkUInt64Lit: directLiteral(tyUInt64) + of nkFloatLit: + if result.typ == nil: + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind in {tyFloat..tyFloat128}): + result.typ = expected + changeType(c, result, expectedType, check=true) + else: + result.typ = getSysType(c.graph, n.info, tyFloat64) + of nkFloat32Lit: directLiteral(tyFloat32) + of nkFloat64Lit: directLiteral(tyFloat64) + of nkFloat128Lit: directLiteral(tyFloat128) + of nkStrLit..nkTripleStrLit: + if result.typ == nil: + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind in {tyString, tyCstring}): + result.typ = expectedType + else: + result.typ = getSysType(c.graph, n.info, tyString) + of nkCharLit: directLiteral(tyChar) + of nkDotExpr: result = semFieldAccess(c, n, flags) if result.kind == nkDotCall: - result.kind = nkCall - result = semExpr(c, result, flags) + result.transitionSonsKind(nkCall) + result = semExpr(c, result, flags, expectedType) of nkBind: - Message(n.info, warnDeprecated, "bind") - result = semExpr(c, n.sons[0], flags) - of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy: + message(c.config, n.info, warnDeprecated, "bind is deprecated") + result = semExpr(c, n[0], flags, expectedType) + of nkTypeOfExpr..nkTupleClassTy, nkStaticTy, nkRefTy..nkEnumTy: + if c.matchedConcept != nil and n.len == 1: + let modifier = n.modifierTypeKindOfNode + if modifier != tyNone: + var baseType = semExpr(c, n[0]).typ.skipTypes({tyTypeDesc}) + result.typ = c.makeTypeDesc(newTypeS(modifier, c, baseType)) + return var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) result.typ = makeTypeDesc(c, typ) - #result = symNodeFromType(c, typ, n.info) - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: + 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) - let mode = if nfDelegate in n.flags: {} else: {checkUndeclared} - var s = qualifiedLookup(c, n.sons[0], mode) - if s != nil: + checkMinSonsLen(n, 1, c.config) + #when defined(nimsuggest): + # if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n) + let mode = if nfDotField in n.flags: {} else: {checkUndeclared} + c.isAmbiguous = false + var s = qualifiedLookUp(c, n[0], mode) + if s != nil: case s.kind - of skMacro: - if sfImmediate notin s.flags: - result = semDirectOp(c, n, flags) - else: - var p = fixImmediateParams(n) - result = semMacroExpr(c, p, p, s) - of skTemplate: - if sfImmediate notin s.flags: - result = semDirectOp(c, n, flags) - else: - var p = fixImmediateParams(n) - result = semTemplateExpr(c, p, s) + 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) + 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 Contains(c.AmbiguousSymbols, s.id): - LocalError(n.info, errUseQualifier, s.name.s) - elif s.magic == mNone: result = semDirectOp(c, n, flags) - else: result = semMagic(c, n, s, flags) - of skProc, skMethod, skConverter, skIterator: - if s.magic == mNone: result = semDirectOp(c, n, flags) - else: result = semMagic(c, n, s, flags) + if ambig: + errorUseQualifier(c, n.info, s) + else: + result = semObjConstr(c, n, flags, expectedType) + elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType) + else: result = semMagic(c, n, s, flags, expectedType) + of skProc, skFunc, skMethod, skConverter, skIterator: + if s.magic == mNone: result = semDirectOp(c, n, flags, expectedType) + else: result = semMagic(c, n, s, flags, expectedType) else: #liMessage(n.info, warnUser, renderTree(n)); - result = semIndirectOp(c, n, flags) - elif isSymChoice(n.sons[0]) or n[0].kind == nkBracketExpr and - isSymChoice(n[0][0]): - result = semDirectOp(c, n, flags) - elif nfDelegate 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) else: result = semWhen(c, n, false) - result = semExpr(c, result, flags) + if result == n: + # This is a "when nimvm" stmt. + result = semWhen(c, n, true) + else: + result = semExpr(c, result, flags, expectedType) of nkBracketExpr: - checkMinSonsLen(n, 1) - var s = qualifiedLookup(c, n.sons[0], {checkUndeclared}) - if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}: - # type parameters: partial generic specialization - n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) - result = explicitGenericInstantiation(c, n, s) - else: - result = semArrayAccess(c, n, flags) + checkMinSonsLen(n, 1, c.config) + result = semArrayAccess(c, n, flags, expectedType) of nkCurlyExpr: - result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags) - of nkPragmaExpr: - # which pragmas are allowed for expressions? `likely`, `unlikely` - internalError(n.info, "semExpr() to implement") # XXX: to implement - of nkPar: - case checkPar(n) + result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags, expectedType) + of nkPragmaExpr: + var + pragma = n[1] + pragmaName = considerQuotedIdent(c, pragma[0]) + flags = flags + finalNodeFlags: TNodeFlags = {} + + case whichKeyword(pragmaName) + of wExplain: + flags.incl efExplain + of wExecuteOnReload: + finalNodeFlags.incl nfExecuteOnReload + else: + # what other pragmas are allowed for expressions? `likely`, `unlikely` + invalidPragma(c, n) + + result = semExpr(c, n[0], flags) + result.flags.incl finalNodeFlags + of nkPar, nkTupleConstr: + case checkPar(c, n) of paNone: result = errorNode(c, n) - of paTuplePositions: result = semTuplePositionsConstr(c, n, flags) - 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) - n.sons[0] = semExprWithType(c, n.sons[0]) - if isAssignable(c, n.sons[0]) notin {arLValue, arLocalLValue}: - LocalError(n.info, errExprHasNoAddress) - n.typ = makePtrType(c, n.sons[0].typ) + checkSonsLen(n, 1, c.config) + result = semAddr(c, n[0]) of nkHiddenAddr, nkHiddenDeref: - checkSonsLen(n, 1) - n.sons[0] = semExpr(c, n.sons[0], flags) + checkSonsLen(n, 1, c.config) + n[0] = semExpr(c, n[0], flags, expectedType) of nkCast: result = semCast(c, n) - of nkIfExpr, nkIfStmt: result = semIf(c, n) - of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: - checkSonsLen(n, 2) - of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: - checkSonsLen(n, 1) - of nkChckRangeF, nkChckRange64, nkChckRange: - checkSonsLen(n, 3) - of nkCheckedFieldExpr: - checkMinSonsLen(n, 2) + of nkIfExpr, nkIfStmt: result = semIf(c, n, flags, expectedType) + of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: + checkSonsLen(n, 2, c.config) + considerGenSyms(c, n) + of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: + checkSonsLen(n, 1, c.config) + considerGenSyms(c, n) + of nkChckRangeF, nkChckRange64, nkChckRange: + checkSonsLen(n, 3, c.config) + considerGenSyms(c, n) + of nkCheckedFieldExpr: + checkMinSonsLen(n, 2, c.config) + considerGenSyms(c, n) of nkTableConstr: - result = semTableConstr(c, n) - of nkClosedSymChoice, nkOpenSymChoice: - # handling of sym choices is context dependent - # the node is left intact for now - 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) + 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 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) of nkMethodDef: result = semMethod(c, n) of nkConverterDef: result = semConverterDef(c, n) of nkMacroDef: result = semMacroDef(c, n) of nkTemplateDef: result = semTemplateDef(c, n) - of nkImportStmt: - if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "import") + of nkImportStmt: + # this particular way allows 'import' in a 'compiles' context so that + # template canImport(x): bool = + # compiles: + # import x + # + # works: + if c.currentScope.depthLevel > 2 + c.compilesContextId: + localError(c.config, n.info, errXOnlyAtModuleScope % "import") result = evalImport(c, n) of nkImportExceptStmt: - if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "import") + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "import") result = evalImportExcept(c, n) - of nkFromStmt: - if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "from") + of nkFromStmt: + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "from") result = evalFrom(c, n) - of nkIncludeStmt: - if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include") + of nkIncludeStmt: + #if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "include") result = evalInclude(c, n) - of nkExportStmt, nkExportExceptStmt: - if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "export") + of nkExportStmt: + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export") result = semExport(c, n) + of nkExportExceptStmt: + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export") + result = semExportExcept(c, n) of nkPragmaBlock: - result = semPragmaBlock(c, n) + result = semPragmaBlock(c, n, expectedType) of nkStaticStmt: result = semStaticStmt(c, n) + of nkDefer: + if c.currentScope == c.topLevelScope: + localError(c.config, n.info, "defer statement not supported at top level") + openScope(c) + n[0] = semExpr(c, n[0]) + closeScope(c) + if not n[0].typ.isEmptyType and not implicitlyDiscardable(n[0]): + localError(c.config, n.info, "'defer' takes a 'void' expression") + #localError(c.config, n.info, errGenerated, "'defer' not allowed in this context") + of nkGotoState, nkState: + if n.len != 1 and n.len != 2: illFormedAst(n, c.config) + for i in 0..<n.len: + n[i] = semExpr(c, n[i]) + of nkComesFrom: discard "ignore the comes from information for now" + of nkMixinStmt: discard + of nkBindStmt: + if c.p != nil: + if n.len > 0 and n[0].kind == nkSym: + c.p.localBindStmts.add n + else: + localError(c.config, n.info, "invalid context for 'bind' statement: " & + renderTree(n, {renderNoComments})) else: - LocalError(n.info, errInvalidExpressionX, + 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()