diff options
author | Daniil Yarancev <21169548+Yardanico@users.noreply.github.com> | 2018-06-05 21:25:45 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-05 21:25:45 +0300 |
commit | 642641359821b6a63c6cf7edaaa45873b7ea59c7 (patch) | |
tree | 627af3020528cb916b3174bd94304307ca875c77 /compiler/semstmts.nim | |
parent | fb44c522e6173528efa8035ecc459c84887d0167 (diff) | |
parent | 3cbc07ac7877b03c605498760fe198e3200cc197 (diff) | |
download | Nim-642641359821b6a63c6cf7edaaa45873b7ea59c7.tar.gz |
Merge pull request #2 from nim-lang/devel
Update
Diffstat (limited to 'compiler/semstmts.nim')
-rw-r--r-- | compiler/semstmts.nim | 922 |
1 files changed, 453 insertions, 469 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index dcaa0263b..3687e50e9 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -10,51 +10,77 @@ ## this module does the semantic checking of statements # included from sem.nim -var enforceVoidContext = PType(kind: tyStmt) +const + errNoSymbolToBorrowFromFound = "no symbol to borrow from found" + errDiscardValueX = "value of type '$1' has to be discarded" + errInvalidDiscard = "statement returns no value that can be discarded" + errInvalidControlFlowX = "invalid control flow: $1" + errSelectorMustBeOfCertainTypes = "selector must be of an ordinal type, float or string" + errExprCannotBeRaised = "only a 'ref object' can be raised" + errBreakOnlyInLoop = "'break' only allowed in loop construct" + errExceptionAlreadyHandled = "exception already handled" + errYieldNotAllowedHere = "'yield' only allowed in an iterator" + errYieldNotAllowedInTryStmt = "'yield' cannot be used within 'try' in a non-inlined iterator" + errInvalidNumberOfYieldExpr = "invalid number of 'yield' expressions" + errCannotReturnExpr = "current routine cannot return an expression" + errGenericLambdaNotAllowed = "A nested proc can have generic parameters only when " & + "it is used as an operand to another routine and the types " & + "of the generic paramers can be inferred from the expected signature." + errCannotInferTypeOfTheLiteral = "cannot infer the type of the $1" + errCannotInferReturnType = "cannot infer the return type of the proc" + errCannotInferStaticParam = "cannot infer the value of the static param '$1'" + errProcHasNoConcreteType = "'$1' doesn't have a concrete type, due to unspecified generic parameters." + errLetNeedsInit = "'let' symbol requires an initialization" + errThreadvarCannotInit = "a thread var cannot be initialized explicitly; this would only run for the main thread" + errImplOfXexpected = "implementation of '$1' expected" + errRecursiveDependencyX = "recursive dependency: '$1'" + errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1" + +var enforceVoidContext = PType(kind: tyStmt) # XXX global variable here proc semDiscard(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) if n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) - if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone: - localError(n.info, errInvalidDiscard) + if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone or n.sons[0].kind == nkTypeOfExpr: + localError(c.config, n.info, errInvalidDiscard) proc semBreakOrContinue(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) if n.sons[0].kind != nkEmpty: if n.kind != nkContinueStmt: var s: PSym case n.sons[0].kind of nkIdent: s = lookUp(c, n.sons[0]) of nkSym: s = n.sons[0].sym - else: illFormedAst(n) + else: illFormedAst(n, c.config) s = getGenSym(c, s) if s.kind == skLabel and s.owner.id == c.p.owner.id: var x = newSymNode(s) x.info = n.info incl(s.flags, sfUsed) n.sons[0] = x - suggestSym(x.info, s, c.graph.usageSym) + suggestSym(c.config, x.info, s, c.graph.usageSym) styleCheckUse(x.info, s) else: - localError(n.info, errInvalidControlFlowX, s.name.s) + localError(c.config, n.info, errInvalidControlFlowX % s.name.s) else: - localError(n.info, errGenerated, "'continue' cannot have a label") + localError(c.config, n.info, errGenerated, "'continue' cannot have a label") elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): - localError(n.info, errInvalidControlFlowX, + localError(c.config, n.info, errInvalidControlFlowX % renderTree(n, {renderNoComments})) -proc semAsm(con: PContext, n: PNode): PNode = - checkSonsLen(n, 2) - var marker = pragmaAsm(con, n.sons[0]) +proc semAsm(c: PContext, n: PNode): PNode = + checkSonsLen(n, 2, c.config) + var marker = pragmaAsm(c, n.sons[0]) if marker == '\0': marker = '`' # default marker - result = semAsmOrEmit(con, n, marker) + result = semAsmOrEmit(c, n, marker) proc semWhile(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) openScope(c) n.sons[0] = forceBool(c, semExprWithType(c, n.sons[0])) inc(c.p.nestedLoopCounter) @@ -71,38 +97,13 @@ proc toCover(t: PType): BiggestInt = else: result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) -when false: - proc performProcvarCheck(c: PContext, info: TLineInfo, s: PSym) = - ## Checks that the given symbol is a proper procedure variable, meaning - ## that it - var smoduleId = getModule(s).id - if sfProcvar notin s.flags and s.typ.callConv == ccDefault and - smoduleId != c.module.id: - block outer: - for module in c.friendModules: - if smoduleId == module.id: - break outer - localError(info, errXCannotBePassedToProcVar, s.name.s) - -template semProcvarCheck(c: PContext, n: PNode) = - when false: - var n = n.skipConv - if n.kind in nkSymChoices: - for x in n: - if x.sym.kind in {skProc, skMethod, skConverter, skIterator}: - performProcvarCheck(c, n.info, x.sym) - elif n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter, - skIterator}: - performProcvarCheck(c, n.info, n.sym) - proc semProc(c: PContext, n: PNode): PNode proc semExprBranch(c: PContext, n: PNode): PNode = result = semExpr(c, n) if result.typ != nil: # XXX tyGenericInst here? - semProcvarCheck(c, result) - if result.typ.kind == tyVar: result = newDeref(result) + if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) proc semExprBranchScope(c: PContext, n: PNode): PNode = openScope(c) @@ -120,39 +121,35 @@ proc implicitlyDiscardable(n: PNode): bool = result = isCallExpr(n) and n.sons[0].kind == nkSym and sfDiscardable in n.sons[0].sym.flags -proc fixNilType(n: PNode) = +proc fixNilType(c: PContext; n: PNode) = if isAtom(n): if n.kind != nkNilLit and n.typ != nil: - localError(n.info, errDiscardValueX, n.typ.typeToString) + localError(c.config, n.info, errDiscardValueX % n.typ.typeToString) elif n.kind in {nkStmtList, nkStmtListExpr}: n.kind = nkStmtList - for it in n: fixNilType(it) + for it in n: fixNilType(c, it) n.typ = nil proc discardCheck(c: PContext, result: PNode) = if c.matchedConcept != nil: return if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: - if result.kind == nkNilLit: - result.typ = nil - message(result.info, warnNilStatement) - elif implicitlyDiscardable(result): + if implicitlyDiscardable(result): var n = result result.typ = nil while n.kind in skipForDiscardable: n = n.lastSon n.typ = nil - elif result.typ.kind != tyError and gCmd != cmdInteractive: - if result.typ.kind == tyNil: - fixNilType(result) - message(result.info, warnNilStatement) - else: - var n = result - while n.kind in skipForDiscardable: n = n.lastSon - var s = "expression '" & $n & "' is of type '" & - result.typ.typeToString & "' and has to be discarded" - if result.typ.kind == tyProc: - s.add "; for a function call use ()" - localError(n.info, s) + elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: + var n = result + while n.kind in skipForDiscardable: n = n.lastSon + var s = "expression '" & $n & "' is of type '" & + result.typ.typeToString & "' and has to be discarded" + if result.info.line != n.info.line or + result.info.fileIndex != n.info.fileIndex: + s.add "; start of expression here: " & $result.info + if result.typ.kind == tyProc: + s.add "; for a function call use ()" + localError(c.config, n.info, s) proc semIf(c: PContext, n: PNode): PNode = result = n @@ -171,7 +168,7 @@ proc semIf(c: PContext, n: PNode): PNode = hasElse = true it.sons[0] = semExprBranchScope(c, it.sons[0]) typ = commonType(typ, it.sons[0]) - else: illFormedAst(it) + else: illFormedAst(it, c.config) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse: for it in n: discardCheck(c, it.lastSon) result.kind = nkIfStmt @@ -187,7 +184,7 @@ proc semIf(c: PContext, n: PNode): PNode = proc semCase(c: PContext, n: PNode): PNode = result = n - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) openScope(c) n.sons[0] = semExprWithType(c, n.sons[0]) var chckCovered = false @@ -201,23 +198,23 @@ proc semCase(c: PContext, n: PNode): PNode = of tyFloat..tyFloat128, tyString, tyError: discard else: - localError(n.info, errSelectorMustBeOfCertainTypes) + localError(c.config, n.info, errSelectorMustBeOfCertainTypes) return for i in countup(1, sonsLen(n) - 1): var x = n.sons[i] when defined(nimsuggest): - if gIdeCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum: + if c.config.ideCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum: suggestEnum(c, x, caseTyp) case x.kind of nkOfBranch: - checkMinSonsLen(x, 2) + checkMinSonsLen(x, 2, c.config) semCaseBranch(c, n, x, i, covered) var last = sonsLen(x)-1 x.sons[last] = semExprBranchScope(c, x.sons[last]) typ = commonType(typ, x.sons[last]) of nkElifBranch: chckCovered = false - checkSonsLen(x, 2) + checkSonsLen(x, 2, c.config) when newScopeForIf: openScope(c) x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0])) when not newScopeForIf: openScope(c) @@ -226,17 +223,17 @@ proc semCase(c: PContext, n: PNode): PNode = closeScope(c) of nkElse: chckCovered = false - checkSonsLen(x, 1) + checkSonsLen(x, 1, c.config) x.sons[0] = semExprBranchScope(c, x.sons[0]) typ = commonType(typ, x.sons[0]) hasElse = true else: - illFormedAst(x) + illFormedAst(x, c.config) if chckCovered: if covered == toCover(n.sons[0].typ): hasElse = true else: - localError(n.info, errNotAllCasesCovered) + localError(c.config, n.info, "not all cases are covered") closeScope(c) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse: for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) @@ -252,69 +249,73 @@ proc semCase(c: PContext, n: PNode): PNode = result.typ = typ proc semTry(c: PContext, n: PNode): PNode = + + var check = initIntSet() + template semExceptBranchType(typeNode: PNode): bool = + # returns true if exception type is imported type + let typ = semTypeNode(c, typeNode, nil).toObject() + var is_imported = false + if isImportedException(typ, c.config): + is_imported = true + elif not isException(typ): + localError(c.config, typeNode.info, errExprCannotBeRaised) + + if containsOrIncl(check, typ.id): + localError(c.config, typeNode.info, errExceptionAlreadyHandled) + typeNode = newNodeIT(nkType, typeNode.info, typ) + is_imported + result = n inc c.p.inTryStmt - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) var typ = commonTypeBegin - n.sons[0] = semExprBranchScope(c, n.sons[0]) - typ = commonType(typ, n.sons[0].typ) + n[0] = semExprBranchScope(c, n[0]) + typ = commonType(typ, n[0].typ) - var check = initIntSet() var last = sonsLen(n) - 1 for i in countup(1, last): - var a = n.sons[i] - checkMinSonsLen(a, 1) - var length = sonsLen(a) + let a = n.sons[i] + checkMinSonsLen(a, 1, c.config) openScope(c) if a.kind == nkExceptBranch: - # so that ``except [a, b, c]`` is supported: - if length == 2 and a.sons[0].kind == nkBracket: - a.sons[0..0] = a.sons[0].sons - length = a.sonsLen - - # Iterate through each exception type in the except branch. - for j in countup(0, length-2): - var typeNode = a.sons[j] # e.g. `Exception` - var symbolNode: PNode = nil # e.g. `foobar` - # Handle the case where the `Exception as foobar` syntax is used. - if typeNode.isInfixAs(): - typeNode = a.sons[j].sons[1] - symbolNode = a.sons[j].sons[2] - - # Resolve the type ident into a PType. - var typ = semTypeNode(c, typeNode, nil).toObject() - if typ.kind != tyObject: - localError(a.sons[j].info, errExprCannotBeRaised) - - let newTypeNode = newNodeI(nkType, typeNode.info) - newTypeNode.typ = typ - if symbolNode.isNil: - a.sons[j] = newTypeNode - else: - a.sons[j].sons[1] = newTypeNode - # Add the exception ident to the symbol table. - let symbol = newSymG(skLet, symbolNode, c) - symbol.typ = typ.toRef() - addDecl(c, symbol) - # Overwrite symbol in AST with the symbol in the symbol table. - let symNode = newNodeI(nkSym, typeNode.info) - symNode.sym = symbol - a.sons[j].sons[2] = symNode - - if containsOrIncl(check, typ.id): - localError(a.sons[j].info, errExceptionAlreadyHandled) + + if a.len == 2 and a[0].kind == nkBracket: + # rewrite ``except [a, b, c]: body`` -> ```except a, b, c: body``` + a.sons[0..0] = a[0].sons + + if a.len == 2 and a[0].isInfixAs(): + # support ``except Exception as ex: body`` + let is_imported = semExceptBranchType(a[0][1]) + let symbol = newSymG(skLet, a[0][2], c) + symbol.typ = if is_imported: a[0][1].typ + else: a[0][1].typ.toRef() + addDecl(c, symbol) + # Overwrite symbol in AST with the symbol in the symbol table. + a[0][2] = newSymNode(symbol, a[0][2].info) + + else: + # support ``except KeyError, ValueError, ... : body`` + var is_native, is_imported: bool + for j in 0..a.len-2: + let tmp = semExceptBranchType(a[j]) + if tmp: is_imported = true + else: is_native = true + + if is_native and is_imported: + localError(c.config, a[0].info, "Mix of imported and native exception types is not allowed in one except branch") + elif a.kind != nkFinally: - illFormedAst(n) + illFormedAst(n, c.config) # last child of an nkExcept/nkFinally branch is a statement: - a.sons[length-1] = semExprBranchScope(c, a.sons[length-1]) - if a.kind != nkFinally: typ = commonType(typ, a.sons[length-1].typ) + a[^1] = semExprBranchScope(c, a[^1]) + if a.kind != nkFinally: typ = commonType(typ, a[^1]) else: dec last closeScope(c) dec c.p.inTryStmt - if isEmptyType(typ) or typ.kind == tyNil: + if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}: discardCheck(c, n.sons[0]) for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) if typ == enforceVoidContext: @@ -337,10 +338,10 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result.info = n.info result.typ = typ else: - changeType(r1, typ, check=true) + changeType(c, r1, typ, check=true) result = r1 elif not sameType(result.typ, typ): - changeType(result, typ, check=false) + changeType(c, result, typ, check=false) proc findShadowedVar(c: PContext, v: PSym): PSym = for scope in walkScopes(c.currentScope.parent): @@ -364,44 +365,23 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = result = semIdentWithPragma(c, kind, n, {}) if result.owner.kind == skModule: incl(result.flags, sfGlobal) - suggestSym(n.info, result, c.graph.usageSym) + suggestSym(c.config, n.info, result, c.graph.usageSym) styleCheckDef(result) -proc checkNilable(v: PSym) = +proc checkNilable(c: PContext; v: PSym) = if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and {tfNotNil, tfNeedsInit} * v.typ.flags != {}: if v.ast.isNil: - message(v.info, warnProveInit, v.name.s) + message(c.config, v.info, warnProveInit, v.name.s) elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags: - message(v.info, warnProveInit, v.name.s) + message(c.config, v.info, warnProveInit, v.name.s) include semasgn -proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = - # consider this: - # var - # x = 0 - # withOverloadedAssignment = foo() - # y = use(withOverloadedAssignment) - # We need to split this into a statement list with multiple 'var' sections - # in order for this transformation to be correct. +proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) = let L = identDefs.len let value = identDefs[L-1] - if value.typ != nil and tfHasAsgn in value.typ.flags and not newDestructors: - # the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()': - identDefs.sons[L-1] = emptyNode - if result.kind != nkStmtList: - let oldResult = result - oldResult.add identDefs - result = newNodeI(nkStmtList, result.info) - result.add oldResult - else: - let o = copyNode(orig) - o.add identDefs - result.add o - for i in 0 .. L-3: - result.add overloadedAsgn(c, identDefs[i], value) - elif result.kind == nkStmtList: + if result.kind == nkStmtList: let o = copyNode(orig) o.add identDefs result.add o @@ -415,13 +395,13 @@ proc isDiscardUnderscore(v: PSym): bool = proc semUsing(c: PContext; n: PNode): PNode = result = ast.emptyNode - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "using") + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "using") for i in countup(0, sonsLen(n)-1): var a = n.sons[i] - if gCmd == cmdIdeTools: suggestStmt(c, a) + if c.config.cmd == cmdIdeTools: suggestStmt(c, a) if a.kind == nkCommentStmt: continue - if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a) - checkMinSonsLen(a, 3) + if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var length = sonsLen(a) if a.sons[length-2].kind != nkEmpty: let typ = semTypeNode(c, a.sons[length-2], nil) @@ -430,10 +410,10 @@ proc semUsing(c: PContext; n: PNode): PNode = v.typ = typ strTableIncl(c.signatures, v) else: - localError(a.info, "'using' section must have a type") + localError(c.config, a.info, "'using' section must have a type") var def: PNode if a.sons[length-1].kind != nkEmpty: - localError(a.info, "'using' sections cannot contain assignments") + localError(c.config, a.info, "'using' sections cannot contain assignments") proc hasEmpty(typ: PType): bool = if typ.kind in {tySequence, tyArray, tySet}: @@ -446,23 +426,23 @@ proc makeDeref(n: PNode): PNode = var t = n.typ if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass: t = t.lastSon - t = skipTypes(t, {tyGenericInst, tyAlias}) + t = skipTypes(t, {tyGenericInst, tyAlias, tySink}) result = n - if t.kind == tyVar: + if t.kind in {tyVar, tyLent}: result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) addSon(result, n) - t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) + t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink}) while t.kind in {tyPtr, tyRef}: var a = result let baseTyp = t.lastSon result = newNodeIT(nkHiddenDeref, n.info, baseTyp) addSon(result, a) - t = skipTypes(baseTyp, {tyGenericInst, tyAlias}) + t = skipTypes(baseTyp, {tyGenericInst, tyAlias, tySink}) proc fillPartialObject(c: PContext; n: PNode; typ: PType) = if n.len == 2: let x = semExprWithType(c, n[0]) - let y = considerQuotedIdent(n[1]) + let y = considerQuotedIdent(c.config, n[1]) let obj = x.typ.skipTypes(abstractPtrs) if obj.kind == tyObject and tfPartial in obj.flags: let field = newSym(skField, getIdent(y.s), obj.sym, n[1].info) @@ -473,14 +453,14 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) = n.sons[1] = newSymNode(field) n.typ = field.typ else: - localError(n.info, "implicit object field construction " & + localError(c.config, n.info, "implicit object field construction " & "requires a .partial object, but got " & typeToString(obj)) else: - localError(n.info, "nkDotNode requires 2 children") + localError(c.config, n.info, "nkDotNode requires 2 children") -proc setVarType(v: PSym, typ: PType) = +proc setVarType(c: PContext; v: PSym, typ: PType) = if v.typ != nil and not sameTypeOrNil(v.typ, typ): - localError(v.info, "inconsistent typing for reintroduced symbol '" & + localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" & v.name.s & "': previous type was: " & typeToString(v.typ) & "; new type is: " & typeToString(typ)) v.typ = typ @@ -491,10 +471,10 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var hasCompileTime = false for i in countup(0, sonsLen(n)-1): var a = n.sons[i] - if gCmd == cmdIdeTools: suggestStmt(c, a) + if c.config.cmd == cmdIdeTools: suggestStmt(c, a) if a.kind == nkCommentStmt: continue - if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a) - checkMinSonsLen(a, 3) + if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var length = sonsLen(a) var typ: PType if a.sons[length-2].kind != nkEmpty: @@ -506,7 +486,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = def = semExprWithType(c, a.sons[length-1], {efAllowDestructor}) if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: # prevent the all too common 'var x = int' bug: - localError(def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?") + localError(c.config, def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?") def.typ = errorType(c) if typ != nil: if typ.isMetaType: @@ -522,32 +502,31 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass: typ = typ.lastSon if hasEmpty(typ): - localError(def.info, errCannotInferTypeOfTheLiteral, + localError(c.config, def.info, errCannotInferTypeOfTheLiteral % ($typ.kind).substr(2).toLowerAscii) elif typ.kind == tyProc and tfUnresolved in typ.flags: - localError(def.info, errProcHasNoConcreteType, def.renderTree) + localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree) else: - if symkind == skLet: localError(a.info, errLetNeedsInit) + if symkind == skLet: localError(c.config, a.info, errLetNeedsInit) # this can only happen for errornous var statements: if typ == nil: continue - typeAllowedCheck(a.info, typ, symkind) + typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {}) liftTypeBoundOps(c, typ, a.info) - var tup = skipTypes(typ, {tyGenericInst, tyAlias}) + var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink}) if a.kind == nkVarTuple: if tup.kind != tyTuple: - localError(a.info, errXExpected, "tuple") + localError(c.config, a.info, errXExpected, "tuple") elif length-2 != sonsLen(tup): - localError(a.info, errWrongNumberOfVariables) - else: - b = newNodeI(nkVarTuple, a.info) - newSons(b, length) - b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator - b.sons[length-1] = def - addToVarSection(c, result, n, b) - elif tup.kind == tyTuple and def.kind == nkPar and + localError(c.config, a.info, errWrongNumberOfVariables) + b = newNodeI(nkVarTuple, a.info) + newSons(b, length) + b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator + b.sons[length-1] = def + addToVarSection(c, result, n, b) + elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and a.kind == nkIdentDefs and a.len > 3: - message(a.info, warnEachIdentIsTuple) + message(c.config, a.info, warnEachIdentIsTuple) for j in countup(0, length-3): if a[j].kind == nkDotExpr: @@ -565,19 +544,19 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if shadowed != nil: shadowed.flags.incl(sfShadowed) if shadowed.kind == skResult and sfGenSym notin v.flags: - message(a.info, warnResultShadowed) + message(c.config, a.info, warnResultShadowed) # a shadowed variable is an error unless it appears on the right # side of the '=': - if warnShadowIdent in gNotes and not identWithin(def, v.name): - message(a.info, warnShadowIdent, v.name.s) + if warnShadowIdent in c.config.notes and not identWithin(def, v.name): + message(c.config, a.info, warnShadowIdent, v.name.s) if a.kind != nkVarTuple: if def.kind != nkEmpty: # this is needed for the evaluation pass and for the guard checking: v.ast = def - if sfThread in v.flags: localError(def.info, errThreadvarCannotInit) - setVarType(v, typ) + if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit) + setVarType(c, v, typ) b = newNodeI(nkIdentDefs, a.info) - if importantComments(): + if importantComments(c.config): # keep documentation information: b.comment = a.comment addSon(b, newSymNode(v)) @@ -585,28 +564,31 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addSon(b, copyTree(def)) addToVarSection(c, result, n, b) else: - if def.kind == nkPar: v.ast = def[j] - setVarType(v, tup.sons[j]) + if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] + # bug #7663, for 'nim check' this can be a non-tuple: + if tup.kind == tyTuple: setVarType(c, v, tup.sons[j]) + else: v.typ = tup b.sons[j] = newSymNode(v) - checkNilable(v) + checkNilable(c, v) if sfCompileTime in v.flags: hasCompileTime = true - if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result) + if hasCompileTime: + vm.setupCompileTimeVar(c.module, c.cache, c.graph, result) proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if gCmd == cmdIdeTools: suggestStmt(c, a) + if c.config.cmd == cmdIdeTools: suggestStmt(c, a) if a.kind == nkCommentStmt: continue - if (a.kind != nkConstDef): illFormedAst(a) - checkSonsLen(a, 3) + if a.kind != nkConstDef: illFormedAst(a, c.config) + checkSonsLen(a, 3, c.config) var v = semIdentDef(c, a.sons[0], skConst) var typ: PType = nil if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil) var def = semConstExpr(c, a.sons[2]) if def == nil: - localError(a.sons[2].info, errConstExprExpected) + localError(c.config, a.sons[2].info, errConstExprExpected) continue # check type compatibility between def.typ and typ: if typ != nil: @@ -614,16 +596,16 @@ proc semConst(c: PContext, n: PNode): PNode = else: typ = def.typ if typ == nil: - localError(a.sons[2].info, errConstExprExpected) + localError(c.config, a.sons[2].info, errConstExprExpected) continue if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit: - localError(a.info, "invalid type for const: " & typeToString(typ)) + localError(c.config, a.info, "invalid type for const: " & typeToString(typ)) continue - setVarType(v, typ) + setVarType(c, v, typ) v.ast = def # no need to copy if sfGenSym notin v.flags: addInterfaceDecl(c, v) var b = newNodeI(nkConstDef, a.info) - if importantComments(): b.comment = a.comment + if importantComments(c.config): b.comment = a.comment addSon(b, newSymNode(v)) addSon(b, a.sons[1]) addSon(b, copyTree(def)) @@ -632,12 +614,12 @@ proc semConst(c: PContext, n: PNode): PNode = include semfields proc addForVarDecl(c: PContext, v: PSym) = - if warnShadowIdent in gNotes: + if warnShadowIdent in c.config.notes: let shadowed = findShadowedVar(c, v) if shadowed != nil: # XXX should we do this here? #shadowed.flags.incl(sfShadowed) - message(v.info, warnShadowIdent, v.name.s) + message(c.config, v.info, warnShadowIdent, v.name.s) addDecl(c, v) proc symForVar(c: PContext, n: PNode): PSym = @@ -649,7 +631,7 @@ proc semForVars(c: PContext, n: PNode): PNode = result = n var length = sonsLen(n) let iterBase = n.sons[length-2].typ - var iter = skipTypes(iterBase, {tyGenericInst, tyAlias}) + var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink}) # length == 3 means that there is one for loop variable # and thus no tuple unpacking: if iter.kind != tyTuple or length == 3: @@ -663,9 +645,9 @@ proc semForVars(c: PContext, n: PNode): PNode = n.sons[0] = newSymNode(v) if sfGenSym notin v.flags: addForVarDecl(c, v) else: - localError(n.info, errWrongNumberOfVariables) + localError(c.config, n.info, errWrongNumberOfVariables) elif length-2 != sonsLen(iter): - localError(n.info, errWrongNumberOfVariables) + localError(c.config, n.info, errWrongNumberOfVariables) else: for i in countup(0, length - 3): var v = symForVar(c, n.sons[i]) @@ -683,19 +665,63 @@ proc semForVars(c: PContext, n: PNode): PNode = proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = result = newNodeI(nkCall, arg.info) result.add(newIdentNode(it.getIdent, arg.info)) - if arg.typ != nil and arg.typ.kind == tyVar: + if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}: result.add newDeref(arg) else: result.add arg result = semExprNoDeref(c, result, {efWantIterator}) +proc isTrivalStmtExpr(n: PNode): bool = + for i in 0 .. n.len-2: + if n[i].kind notin {nkEmpty, nkCommentStmt}: + return false + result = true + +proc handleForLoopMacro(c: PContext; n: PNode): PNode = + let iterExpr = n[^2] + if iterExpr.kind in nkCallKinds: + # we transform + # n := for a, b, c in m(x, y, z): Y + # to + # m(n) + let forLoopStmt = magicsys.getCompilerProc(c.graph, "ForLoopStmt") + if forLoopStmt == nil: return + + let headSymbol = iterExpr[0] + var o: TOverloadIter + var match: PSym = nil + var symx = initOverloadIter(o, c, headSymbol) + while symx != nil: + if symx.kind in {skTemplate, skMacro}: + if symx.typ.len == 2 and symx.typ[1] == forLoopStmt.typ: + if match == nil: + match = symx + else: + localError(c.config, n.info, errAmbiguousCallXYZ % [ + getProcHeader(match), getProcHeader(symx), $iterExpr]) + symx = nextOverloadIter(o, c, headSymbol) + + if match == nil: return + var callExpr = newNodeI(nkCall, n.info) + callExpr.add newSymNode(match) + callExpr.add n + case match.kind + of skMacro: result = semMacroExpr(c, callExpr, callExpr, match, {}) + of skTemplate: result = semTemplateExpr(c, callExpr, match, {}) + else: result = nil + proc semFor(c: PContext, n: PNode): PNode = - result = n - checkMinSonsLen(n, 3) + checkMinSonsLen(n, 3, c.config) var length = sonsLen(n) + result = handleForLoopMacro(c, n) + if result != nil: return result openScope(c) + result = n n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator}) var call = n.sons[length-2] + if call.kind == nkStmtListExpr and isTrivalStmtExpr(call): + call = call.lastSon + n.sons[length-2] = call let isCallExpr = call.kind in nkCallKinds if isCallExpr and call[0].kind == nkSym and call[0].sym.magic in {mFields, mFieldPairs, mOmpParFor}: @@ -715,7 +741,7 @@ proc semFor(c: PContext, n: PNode): PNode = elif length == 4: n.sons[length-2] = implicitIterator(c, "pairs", n.sons[length-2]) else: - localError(n.sons[length-2].info, errIteratorExpected) + localError(c.config, n.sons[length-2].info, "iterator within for loop context expected") result = semForVars(c, n) else: result = semForVars(c, n) @@ -726,29 +752,32 @@ proc semFor(c: PContext, n: PNode): PNode = proc semRaise(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 1) - if n.sons[0].kind != nkEmpty: - n.sons[0] = semExprWithType(c, n.sons[0]) - var typ = n.sons[0].typ - if typ.kind != tyRef or typ.lastSon.kind != tyObject: - localError(n.info, errExprCannotBeRaised) - - # check if the given object inherits from Exception - var base = typ.lastSon - while true: - if base.sym.magic == mException: - break - if base.lastSon == nil: - localError(n.info, "raised object of type $1 does not inherit from Exception", [typ.sym.name.s]) - return - base = base.lastSon + checkSonsLen(n, 1, c.config) + if n[0].kind != nkEmpty: + n[0] = semExprWithType(c, n[0]) + let typ = n[0].typ + if not isImportedException(typ, c.config): + if typ.kind != tyRef or typ.lastSon.kind != tyObject: + localError(c.config, n.info, errExprCannotBeRaised) + if not isException(typ.lastSon): + localError(c.config, n.info, "raised object of type $1 does not inherit from Exception", + [typeToString(typ)]) proc addGenericParamListToScope(c: PContext, n: PNode) = - if n.kind != nkGenericParams: illFormedAst(n) + if n.kind != nkGenericParams: illFormedAst(n, c.config) for i in countup(0, sonsLen(n)-1): var a = n.sons[i] if a.kind == nkSym: addDecl(c, a.sym) - else: illFormedAst(a) + else: illFormedAst(a, c.config) + +proc typeSectionTypeName(c: PContext; n: PNode): PNode = + if n.kind == nkPragmaExpr: + if n.len == 0: illFormedAst(n, c.config) + result = n.sons[0] + else: + result = n + if result.kind != nkSym: illFormedAst(n, c.config) + proc typeSectionLeftSidePass(c: PContext, n: PNode) = # process the symbols on the left side for the whole type section, before @@ -756,21 +785,21 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] when defined(nimsuggest): - if gCmd == cmdIdeTools: + if c.config.cmd == cmdIdeTools: inc c.inTypeContext suggestStmt(c, a) dec c.inTypeContext if a.kind == nkCommentStmt: continue - if a.kind != nkTypeDef: illFormedAst(a) - checkSonsLen(a, 3) + if a.kind != nkTypeDef: illFormedAst(a, c.config) + checkSonsLen(a, 3, c.config) let name = a.sons[0] var s: PSym if name.kind == nkDotExpr and a[2].kind == nkObjectTy: - let pkgName = considerQuotedIdent(name[0]) - let typName = considerQuotedIdent(name[1]) + let pkgName = considerQuotedIdent(c.config, name[0]) + let typName = considerQuotedIdent(c.config, name[1]) let pkg = c.graph.packageSyms.strTableGet(pkgName) if pkg.isNil or pkg.kind != skPackage: - localError(name.info, "unknown package name: " & pkgName.s) + localError(c.config, name.info, "unknown package name: " & pkgName.s) else: let typsym = pkg.tab.strTableGet(typName) if typsym.isNil: @@ -784,7 +813,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = s = typsym addInterfaceDecl(c, s) else: - localError(name.info, typsym.name.s & " is not a type that can be forwarded") + localError(c.config, name.info, typsym.name.s & " is not a type that can be forwarded") s = typsym else: s = semIdentDef(c, name, skType) @@ -796,7 +825,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = # check if the symbol already exists: let pkg = c.module.owner if not isTopLevel(c) or pkg.isNil: - localError(name.info, "only top level types in a package can be 'package'") + localError(c.config, name.info, "only top level types in a package can be 'package'") else: let typsym = pkg.tab.strTableGet(s.name) if typsym != nil: @@ -804,22 +833,23 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = typeCompleted(typsym) typsym.info = s.info else: - localError(name.info, "cannot complete type '" & s.name.s & "' twice; " & + localError(c.config, name.info, "cannot complete type '" & s.name.s & "' twice; " & "previous type completion was here: " & $typsym.info) s = typsym # add it here, so that recursive types are possible: if sfGenSym notin s.flags: addInterfaceDecl(c, s) - a.sons[0] = newSymNode(s) + if name.kind == nkPragmaExpr: + a.sons[0].sons[0] = newSymNode(s) + else: + a.sons[0] = newSymNode(s) -proc checkCovariantParamsUsages(genericType: PType) = +proc checkCovariantParamsUsages(c: PContext; genericType: PType) = var body = genericType[^1] - proc traverseSubTypes(t: PType): bool = - template error(msg) = localError(genericType.sym.info, msg) - + proc traverseSubTypes(c: PContext; t: PType): bool = + template error(msg) = localError(c.config, genericType.sym.info, msg) result = false - template subresult(r) = let sub = r result = result or sub @@ -828,24 +858,19 @@ proc checkCovariantParamsUsages(genericType: PType) = of tyGenericParam: t.flags.incl tfWeakCovariant return true - of tyObject: for field in t.n: - subresult traverseSubTypes(field.typ) - + subresult traverseSubTypes(c, field.typ) of tyArray: - return traverseSubTypes(t[1]) - + return traverseSubTypes(c, t[1]) of tyProc: for subType in t.sons: if subType != nil: - subresult traverseSubTypes(subType) + subresult traverseSubTypes(c, subType) if result: - error("non-invariant type param used in a proc type: " & $t) - + error("non-invariant type param used in a proc type: " & $t) of tySequence: - return traverseSubTypes(t[0]) - + return traverseSubTypes(c, t[0]) of tyGenericInvocation: let targetBody = t[0] for i in 1 ..< t.len: @@ -866,44 +891,35 @@ proc checkCovariantParamsUsages(genericType: PType) = "' used in a non-contravariant position") result = true else: - subresult traverseSubTypes(param) - + subresult traverseSubTypes(c, param) of tyAnd, tyOr, tyNot, tyStatic, tyBuiltInTypeClass, tyCompositeTypeClass: error("non-invariant type parameters cannot be used with types such '" & $t & "'") - of tyUserTypeClass, tyUserTypeClassInst: error("non-invariant type parameters are not supported in concepts") - of tyTuple: for fieldType in t.sons: - subresult traverseSubTypes(fieldType) - - of tyPtr, tyRef, tyVar: + subresult traverseSubTypes(c, fieldType) + of tyPtr, tyRef, tyVar, tyLent: if t.base.kind == tyGenericParam: return true - return traverseSubTypes(t.base) - - of tyDistinct, tyAlias: - return traverseSubTypes(t.lastSon) - + return traverseSubTypes(c, t.base) + of tyDistinct, tyAlias, tySink: + return traverseSubTypes(c, t.lastSon) of tyGenericInst: - internalAssert false - + internalAssert c.config, false else: discard - - discard traverseSubTypes(body) + discard traverseSubTypes(c, body) proc typeSectionRightSidePass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkTypeDef): illFormedAst(a) - checkSonsLen(a, 3) - let name = a.sons[0] - if (name.kind != nkSym): illFormedAst(a) + if a.kind != nkTypeDef: illFormedAst(a, c.config) + checkSonsLen(a, 3, c.config) + let name = typeSectionTypeName(c, a.sons[0]) var s = name.sym if s.magic == mNone and a.sons[2].kind == nkEmpty: - localError(a.info, errImplOfXexpected, s.name.s) + localError(c.config, a.info, errImplOfXexpected % s.name.s) if s.magic != mNone: processMagicType(c, s) if a.sons[1].kind != nkEmpty: # We have a generic type declaration here. In generic types, @@ -934,7 +950,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = body.size = -1 # could not be computed properly s.typ.sons[sonsLen(s.typ) - 1] = body if tfCovariant in s.typ.flags: - checkCovariantParamsUsages(s.typ) + checkCovariantParamsUsages(c, s.typ) # XXX: This is a temporary limitation: # The codegen currently produces various failures with # generic imported types that have fields, but we need @@ -969,8 +985,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # give anonymous object a dummy symbol: var st = s.typ if st.kind == tyGenericBody: st = st.lastSon - internalAssert st.kind in {tyPtr, tyRef} - internalAssert st.lastSon.sym == nil + internalAssert c.config, st.kind in {tyPtr, tyRef} + internalAssert c.config, st.lastSon.sym == nil incl st.flags, tfRefsAnonObj let obj = newSym(skType, getIdent(s.name.s & ":ObjectType"), getCurrOwner(c), s.info) @@ -978,36 +994,36 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = st.lastSon.sym = obj -proc checkForMetaFields(n: PNode) = +proc checkForMetaFields(c: PContext; n: PNode) = template checkMeta(t) = if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags: - localError(n.info, errTIsNotAConcreteType, t.typeToString) + localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString) if n.isNil: return case n.kind of nkRecList, nkRecCase: - for s in n: checkForMetaFields(s) + for s in n: checkForMetaFields(c, s) of nkOfBranch, nkElse: - checkForMetaFields(n.lastSon) + checkForMetaFields(c, n.lastSon) of nkSym: let t = n.sym.typ case t.kind - of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, - tyProc, tyGenericInvocation, tyGenericInst, tyAlias: + of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, + tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink: let start = int ord(t.kind in {tyGenericInvocation, tyGenericInst}) for i in start ..< t.sons.len: checkMeta(t.sons[i]) else: checkMeta(t) else: - internalAssert false + internalAssert c.config, false proc typeSectionFinalPass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if a.sons[0].kind != nkSym: illFormedAst(a) - var s = a.sons[0].sym + let name = typeSectionTypeName(c, a.sons[0]) + var s = name.sym # compute the type's size and check for illegal recursions: if a.sons[1].kind == nkEmpty: var x = a[2] @@ -1018,7 +1034,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = # type aliases are hard: var t = semTypeNode(c, x, nil) assert t != nil - if s.typ != nil and s.typ.kind != tyAlias: + if s.typ != nil and s.typ.kind notin {tyAlias, tySink}: if t.kind in {tyProc, tyGenericInst} and not t.isMetaType: assignType(s.typ, t) s.typ.id = t.id @@ -1026,9 +1042,9 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assert s.typ != nil assignType(s.typ, t) s.typ.id = t.id # same id - checkConstructedType(s.info, s.typ) + checkConstructedType(c.config, s.info, s.typ) if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: - checkForMetaFields(s.typ.n) + checkForMetaFields(c, s.typ.n) instAllTypeBoundOp(c, n.info) @@ -1037,14 +1053,14 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode = case n.kind of nkIncludeStmt: for i in 0..<n.len: - var f = checkModuleName(n.sons[i]) + var f = checkModuleName(c.config, n.sons[i]) if f != InvalidFileIDX: - if containsOrIncl(c.includedFiles, f): - localError(n.info, errRecursiveDependencyX, f.toFilename) + if containsOrIncl(c.includedFiles, f.int): + localError(c.config, n.info, errRecursiveDependencyX % f.toFilename) else: let code = gIncludeFile(c.graph, c.module, f, c.cache) gatherStmts c, code, result - excl(c.includedFiles, f) + excl(c.includedFiles, f.int) of nkStmtList: for i in 0 ..< n.len: gatherStmts(c, n.sons[i], result) @@ -1096,12 +1112,12 @@ proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) = s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind) if s.kind notin {skMacro, skTemplate}: if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt: - localError(n.info, errGenerated, "invalid return type: 'stmt'") + localError(c.config, n.info, "invalid return type: 'stmt'") proc addParams(c: PContext, n: PNode, kind: TSymKind) = for i in countup(1, sonsLen(n)-1): if n.sons[i].kind == nkSym: addParamOrResult(c, n.sons[i].sym, kind) - else: illFormedAst(n) + else: illFormedAst(n, c.config) proc semBorrow(c: PContext, n: PNode, s: PSym) = # search for the correct alias: @@ -1110,7 +1126,7 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) = # store the alias: n.sons[bodyPos] = newSymNode(b) else: - localError(n.info, errNoSymbolToBorrowFromFound) + localError(c.config, n.info, errNoSymbolToBorrowFromFound) proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) = if t != nil: @@ -1133,7 +1149,7 @@ proc lookupMacro(c: PContext, n: PNode): PSym = result = n.sym if result.kind notin {skMacro, skTemplate}: result = nil else: - result = searchInScopes(c, considerQuotedIdent(n), {skMacro, skTemplate}) + result = searchInScopes(c, considerQuotedIdent(c.config, n), {skMacro, skTemplate}) proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode = @@ -1141,16 +1157,19 @@ proc semProcAnnotation(c: PContext, prc: PNode; if n == nil or n.kind == nkEmpty: return for i in countup(0, n.len-1): var it = n.sons[i] - var key = if it.kind == nkExprColonExpr: it.sons[0] else: it + var key = if it.kind in nkPragmaCallKinds and it.len >= 1: it.sons[0] else: it let m = lookupMacro(c, key) if m == nil: if key.kind == nkIdent and key.ident.id == ord(wDelegator): - if considerQuotedIdent(prc.sons[namePos]).s == "()": + if considerQuotedIdent(c.config, prc.sons[namePos]).s == "()": prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info) prc.sons[pragmasPos] = copyExcept(n, i) else: - localError(prc.info, errOnlyACallOpCanBeDelegator) + localError(c.config, prc.info, "only a call operator can be a delegator") continue + elif sfCustomPragma in m.flags: + continue # semantic check for custom pragma happens later in semProcAux + # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and # let the semantic checker deal with it: var x = newNodeI(nkCall, n.info) @@ -1159,10 +1178,12 @@ proc semProcAnnotation(c: PContext, prc: PNode; if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: prc.sons[pragmasPos] = emptyNode - if it.kind == nkExprColonExpr: - # pass pragma argument to the macro too: - x.add(it.sons[1]) + if it.kind in nkPragmaCallKinds and it.len > 1: + # pass pragma arguments to the macro too: + for i in 1..<it.len: + x.add(it.sons[i]) x.add(prc) + # recursion assures that this works for multiple macro annotations too: result = semExpr(c, x) # since a proc annotation can set pragmas, we process these here again. @@ -1189,7 +1210,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semProcAnnotation(c, n, lambdaPragmas) if result != nil: return result result = n - checkSonsLen(n, bodyPos + 1) + checkSonsLen(n, bodyPos + 1, c.config) var s: PSym if n[namePos].kind != nkSym: s = newSym(skProc, c.cache.idAnon, getCurrOwner(c), n.info) @@ -1206,9 +1227,6 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = gp = newNodeI(nkGenericParams, n.info) if n.sons[paramsPos].kind != nkEmpty: - #if n.kind == nkDo and not experimentalMode(c): - # localError(n.sons[paramsPos].info, - # "use the {.experimental.} pragma to enable 'do' with parameters") semParamList(c, n.sons[paramsPos], gp, s) # paramsTypeCheck(c, s.typ) if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty: @@ -1218,10 +1236,10 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = s.typ = newProcType(c, n.info) if n.sons[pragmasPos].kind != nkEmpty: pragma(c, s, n.sons[pragmasPos], lambdaPragmas) - s.options = gOptions + s.options = c.config.options if n.sons[bodyPos].kind != nkEmpty: if sfImportc in s.flags: - localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) + localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s) #if efDetermineType notin flags: # XXX not good enough; see tnamedparamanonproc.nim if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags): @@ -1229,13 +1247,13 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = addResult(c, s.typ.sons[0], n.info, skProc) addResultNode(c, n) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.module, semBody, s) + n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) popProcCon(c) elif efOperand notin flags: - localError(n.info, errGenericLambdaNotAllowed) + localError(c.config, n.info, errGenericLambdaNotAllowed) sideEffectsCheck(c, s) else: - localError(n.info, errImplOfXexpected, s.name.s) + localError(c.config, n.info, errImplOfXexpected % s.name.s) closeScope(c) # close scope for parameters popOwner(c) result.typ = s.typ @@ -1260,7 +1278,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = for i in 1..<params.len: if params[i].typ.kind in {tyTypeDesc, tyGenericParam, tyFromExpr}+tyTypeClasses: - localError(params[i].info, "cannot infer type of parameter: " & + localError(c.config, params[i].info, "cannot infer type of parameter: " & params[i].sym.name.s) #params[i].sym.owner = s openScope(c) @@ -1270,7 +1288,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = addResult(c, n.typ.sons[0], n.info, skProc) addResultNode(c, n) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.module, semBody, s) + n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) popProcCon(c) popOwner(c) closeScope(c) @@ -1302,27 +1320,26 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) = case s.name.s.normalize - of "destroy", "=destroy": - if newDestructors: - let t = s.typ - var noError = false - if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar: - var obj = t.sons[1].sons[0] - while true: - incl(obj.flags, tfHasAsgn) - if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon - elif obj.kind == tyGenericInvocation: obj = obj.sons[0] - else: break - if obj.kind in {tyObject, tyDistinct}: - if obj.destructor.isNil: - obj.destructor = s - else: - localError(n.info, errGenerated, - "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) - noError = true - if not noError and sfSystemModule notin s.owner.flags: - localError(n.info, errGenerated, - "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") + of "=destroy": + let t = s.typ + var noError = false + if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar: + var obj = t.sons[1].sons[0] + while true: + incl(obj.flags, tfHasAsgn) + if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon + elif obj.kind == tyGenericInvocation: obj = obj.sons[0] + else: break + if obj.kind in {tyObject, tyDistinct}: + if obj.destructor.isNil: + obj.destructor = s + else: + localError(c.config, n.info, errGenerated, + "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) + noError = true + if not noError and sfSystemModule notin s.owner.flags: + localError(c.config, n.info, errGenerated, + "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") incl(s.flags, sfUsed) of "deepcopy", "=deepcopy": if s.typ.len == 2 and @@ -1338,13 +1355,13 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if t.kind in {tyObject, tyDistinct, tyEnum}: if t.deepCopy.isNil: t.deepCopy = s else: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "cannot bind another 'deepCopy' to: " & typeToString(t)) else: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "cannot bind 'deepCopy' to: " & typeToString(t)) else: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") incl(s.flags, sfUsed) of "=", "=sink": @@ -1369,15 +1386,15 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if opr[].isNil: opr[] = s else: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) return if sfSystemModule notin s.owner.flags: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)") else: if sfOverriden in s.flags: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "'destroy' or 'deepCopy' expected for 'override'") proc cursorInProcAux(n: PNode): bool = @@ -1413,14 +1430,14 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = for col in countup(1, sonsLen(tt)-1): let t = tt.sons[col] if t != nil and t.kind == tyGenericInvocation: - var x = skipTypes(t.sons[0], {tyVar, tyPtr, tyRef, tyGenericInst, + var x = skipTypes(t.sons[0], {tyVar, tyLent, tyPtr, tyRef, tyGenericInst, tyGenericInvocation, tyGenericBody, - tyAlias}) + tyAlias, tySink}) if x.kind == tyObject and t.len-1 == n.sons[genericParamsPos].len: foundObj = true x.methods.safeAdd((col,s)) if not foundObj: - message(n.info, warnDeprecated, "generic method not attachable to object type") + message(c.config, n.info, warnDeprecated, "generic method not attachable to object type") else: # why check for the body? bug #2400 has none. Checking for sfForward makes # no sense either. @@ -1428,7 +1445,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = if hasObjParam(s): methodDef(c.graph, s, fromCache=false) else: - localError(n.info, errXNeedsParamObjectType, "method") + localError(c.config, n.info, "'method' needs a parameter that has an object type") proc semProcAux(c: PContext, n: PNode, kind: TSymKind, validPragmas: TSpecialWords, @@ -1436,7 +1453,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, result = semProcAnnotation(c, n, validPragmas) if result != nil: return result result = n - checkSonsLen(n, bodyPos + 1) + checkSonsLen(n, bodyPos + 1, c.config) var s: PSym var typeIsDetermined = false var isAnon = false @@ -1493,7 +1510,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[patternPos] = semPattern(c, n.sons[patternPos]) if s.kind == skIterator: s.typ.flags.incl(tfIterator) - + elif s.kind == skFunc: + incl(s.flags, sfNoSideEffect) + incl(s.typ.flags, tfNoSideEffect) var proto = searchForProc(c, oldScope, s) if proto == nil or isAnon: if s.kind == skIterator: @@ -1523,10 +1542,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # XXX This needs more checks eventually, for example that external # linking names do agree: if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: - localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX, - "'" & proto.name.s & "' from " & $proto.info) + localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % + ("'" & proto.name.s & "' from " & $proto.info)) if sfForward notin proto.flags: - wrongRedefinition(n.info, proto.name.s) + wrongRedefinition(c, n.info, proto.name.s) excl(proto.flags, sfForward) closeScope(c) # close scope with wrong parameter symbols openScope(c) # open scope for old (correct) parameter symbols @@ -1539,30 +1558,32 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[genericParamsPos] = proto.ast.sons[genericParamsPos] n.sons[paramsPos] = proto.ast.sons[paramsPos] n.sons[pragmasPos] = proto.ast.sons[pragmasPos] - if n.sons[namePos].kind != nkSym: internalError(n.info, "semProcAux") + if n.sons[namePos].kind != nkSym: internalError(c.config, n.info, "semProcAux") n.sons[namePos].sym = proto - if importantComments() and not isNil(proto.ast.comment): + if importantComments(c.config) and not isNil(proto.ast.comment): n.comment = proto.ast.comment proto.ast = n # needed for code generation popOwner(c) pushOwner(c, s) - s.options = gOptions + s.options = c.config.options if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if s.name.s[0] in {'.', '('}: - if s.name.s in [".", ".()", ".="] and not experimentalMode(c) and not newDestructors: - message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s) - elif s.name.s == "()" and not experimentalMode(c): - message(n.info, warnDeprecated, "overloaded '()' operators are now .experimental; " & s.name.s) + if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}: + localError(c.config, n.info, "the overloaded " & s.name.s & + " operator has to be enabled with {.experimental: \"dotOperators\".}") + elif s.name.s == "()" and callOperator notin c.features: + localError(c.config, n.info, "the overloaded " & s.name.s & + " operator has to be enabled with {.experimental: \"callOperator\".}") if n.sons[bodyPos].kind != nkEmpty: # for DLL generation it is annoying to check for sfImportc! if sfBorrow in s.flags: - localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) + localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s) let usePseudoGenerics = kind in {skMacro, skTemplate} # Macros and Templates can have generic parameters, but they are # only used for overload resolution (there is no instantiation of # the symbol, so we must process the body now) - if not usePseudoGenerics and gIdeCmd in {ideSug, ideCon} and not + if not usePseudoGenerics and c.config.ideCmd in {ideSug, ideCon} and not cursorInProc(n.sons[bodyPos]): discard "speed up nimsuggest" if s.kind == skMethod: semMethodPrototype(c, s, n) @@ -1580,7 +1601,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) # unfortunately we cannot skip this step when in 'system.compiles' # context as it may even be evaluated in 'system.compiles': - n.sons[bodyPos] = transformBody(c.module, semBody, s) + n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) else: if s.typ.sons[0] != nil and kind != skIterator: addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) @@ -1596,7 +1617,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, popProcCon(c) else: if s.kind == skMethod: semMethodPrototype(c, s, n) - if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s) + if proto != nil: localError(c.config, n.info, errImplOfXexpected % proto.name.s) if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: incl(s.flags, sfForward) elif sfBorrow in s.flags: semBorrow(c, n, s) @@ -1611,7 +1632,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, result.typ = s.typ if isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure: - localError(s.info, "'.closure' calling convention for top level routines is invalid") + localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid") proc determineType(c: PContext, s: PSym) = if s.typ != nil: return @@ -1627,12 +1648,16 @@ proc semIterator(c: PContext, n: PNode): PNode = n[namePos].sym.owner = getCurrOwner(c) n[namePos].sym.kind = skIterator result = semProcAux(c, n, skIterator, iteratorPragmas) + # bug #7093: if after a macro transformation we don't have an + # nkIteratorDef aynmore, return. The iterator then might have been + # sem'checked already. (Or not, if the macro skips it.) + if result.kind != n.kind: return var s = result.sons[namePos].sym var t = s.typ if t.sons[0] == nil and s.typ.callConv != ccClosure: - localError(n.info, errXNeedsReturnType, "iterator") + localError(c.config, n.info, "iterator needs a return type") if isAnon and s.typ.callConv == ccInline: - localError(n.info, "inline iterators are not first-class / cannot be assigned to variables") + localError(c.config, n.info, "inline iterators are not first-class / cannot be assigned to variables") # iterators are either 'inline' or 'closure'; for backwards compatibility, # we require first class iterators to be marked with 'closure' explicitly # -- at least for 0.9.2. @@ -1640,13 +1665,8 @@ proc semIterator(c: PContext, n: PNode): PNode = incl(s.typ.flags, tfCapturesEnv) else: s.typ.callConv = ccInline - when false: - if s.typ.callConv != ccInline: - s.typ.callConv = ccClosure - # and they always at least use the 'env' for the state field: - incl(s.typ.flags, tfCapturesEnv) if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone: - localError(n.info, errImplOfXexpected, s.name.s) + localError(c.config, n.info, errImplOfXexpected % s.name.s) proc semProc(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skProc, procPragmas) @@ -1655,10 +1675,14 @@ proc semFunc(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skFunc, procPragmas) proc semMethod(c: PContext, n: PNode): PNode = - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method") + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "method") result = semProcAux(c, n, skMethod, methodPragmas) # macros can transform converters to nothing: if namePos >= result.safeLen: return result + # bug #7093: if after a macro transformation we don't have an + # nkIteratorDef aynmore, return. The iterator then might have been + # sem'checked already. (Or not, if the macro skips it.) + if result.kind != nkMethodDef: return var s = result.sons[namePos].sym # we need to fix the 'auto' return type for the dispatcher here (see tautonotgeneric # test case): @@ -1672,22 +1696,30 @@ proc semMethod(c: PContext, n: PNode): PNode = else: disp.ast[resultPos].sym.typ = ret proc semConverterDef(c: PContext, n: PNode): PNode = - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter") - checkSonsLen(n, bodyPos + 1) + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "converter") + checkSonsLen(n, bodyPos + 1, c.config) result = semProcAux(c, n, skConverter, converterPragmas) # macros can transform converters to nothing: if namePos >= result.safeLen: return result + # bug #7093: if after a macro transformation we don't have an + # nkIteratorDef aynmore, return. The iterator then might have been + # sem'checked already. (Or not, if the macro skips it.) + if result.kind != nkConverterDef: return var s = result.sons[namePos].sym var t = s.typ - if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "converter") - if sonsLen(t) != 2: localError(n.info, errXRequiresOneArgument, "converter") + if t.sons[0] == nil: localError(c.config, n.info, errXNeedsReturnType % "converter") + if sonsLen(t) != 2: localError(c.config, n.info, "a converter takes exactly one argument") addConverter(c, s) proc semMacroDef(c: PContext, n: PNode): PNode = - checkSonsLen(n, bodyPos + 1) + checkSonsLen(n, bodyPos + 1, c.config) result = semProcAux(c, n, skMacro, macroPragmas) # macros can transform macros to nothing: if namePos >= result.safeLen: return result + # bug #7093: if after a macro transformation we don't have an + # nkIteratorDef aynmore, return. The iterator then might have been + # sem'checked already. (Or not, if the macro skips it.) + if result.kind != nkMacroDef: return var s = result.sons[namePos].sym var t = s.typ var allUntyped = true @@ -1695,21 +1727,21 @@ proc semMacroDef(c: PContext, n: PNode): PNode = let param = t.n.sons[i].sym if param.typ.kind != tyExpr: allUntyped = false if allUntyped: incl(s.flags, sfAllUntyped) - if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro") + if t.sons[0] == nil: localError(c.config, n.info, "macro needs a return type") if n.sons[bodyPos].kind == nkEmpty: - localError(n.info, errImplOfXexpected, s.name.s) + localError(c.config, n.info, errImplOfXexpected % s.name.s) proc evalInclude(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) addSon(result, n) for i in countup(0, sonsLen(n) - 1): - var f = checkModuleName(n.sons[i]) + var f = checkModuleName(c.config, n.sons[i]) if f != InvalidFileIDX: - if containsOrIncl(c.includedFiles, f): - localError(n.info, errRecursiveDependencyX, f.toFilename) + if containsOrIncl(c.includedFiles, f.int): + localError(c.config, n.info, errRecursiveDependencyX % f.toFilename) else: addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache))) - excl(c.includedFiles, f) + excl(c.includedFiles, f.int) proc setLine(n: PNode, info: TLineInfo) = for i in 0 ..< safeLen(n): setLine(n.sons[i], info) @@ -1733,19 +1765,13 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = proc semStaticStmt(c: PContext, n: PNode): PNode = #echo "semStaticStmt" #writeStackTrace() + inc c.inStaticContext let a = semStmt(c, n.sons[0]) + dec c.inStaticContext n.sons[0] = a - evalStaticStmt(c.module, c.cache, a, c.p.owner) + evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner) result = newNodeI(nkDiscardStmt, n.info, 1) result.sons[0] = emptyNode - when false: - result = evalStaticStmt(c.module, a, c.p.owner) - if result.isNil: - LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) - result = emptyNode - elif result.kind == nkEmpty: - result = newNodeI(nkDiscardStmt, n.info, 1) - result.sons[0] = emptyNode proc usesResult(n: PNode): bool = # nkStmtList(expr) properly propagates the void context, @@ -1764,7 +1790,7 @@ proc inferConceptStaticParam(c: PContext, inferred, n: PNode) = var typ = inferred.typ let res = semConstExpr(c, n) if not sameType(res.typ, typ.base): - localError(n.info, + localError(c.config, n.info, "cannot infer the concept parameter '%s', due to a type mismatch. " & "attempt to equate '%s' and '%s'.", [inferred.renderTree, $res.typ, $typ.base]) @@ -1787,80 +1813,47 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = # nkNilLit, nkEmpty}: # dec last for i in countup(0, length - 1): - let k = n.sons[i].kind - case k - of nkFinally, nkExceptBranch: - # stand-alone finally and except blocks are - # transformed into regular try blocks: - # - # var f = fopen("somefile") | var f = fopen("somefile") - # finally: fclose(f) | try: - # ... | ... - # | finally: - # | fclose(f) - var deferPart: PNode - if k == nkDefer: - deferPart = newNodeI(nkFinally, n.sons[i].info) - deferPart.add n.sons[i].sons[0] - elif k == nkFinally: - message(n.info, warnDeprecated, - "use 'defer'; standalone 'finally'") - deferPart = n.sons[i] - else: - message(n.info, warnDeprecated, - "use an explicit 'try'; standalone 'except'") - deferPart = n.sons[i] - var tryStmt = newNodeI(nkTryStmt, n.sons[i].info) - var body = newNodeI(nkStmtList, n.sons[i].info) - if i < n.sonsLen - 1: - body.sons = n.sons[(i+1)..n.len-1] - tryStmt.addSon(body) - tryStmt.addSon(deferPart) - n.sons[i] = semTry(c, tryStmt) - n.sons.setLen(i+1) + var expr = semExpr(c, n.sons[i], flags) + n.sons[i] = expr + if c.matchedConcept != nil and expr.typ != nil and + (nfFromTemplate notin n.flags or i != last): + case expr.typ.kind + of tyBool: + if expr.kind == nkInfix and + expr[0].kind == nkSym and + expr[0].sym.name.s == "==": + if expr[1].typ.isUnresolvedStatic: + inferConceptStaticParam(c, expr[1], expr[2]) + continue + elif expr[2].typ.isUnresolvedStatic: + inferConceptStaticParam(c, expr[2], expr[1]) + continue + + let verdict = semConstExpr(c, n[i]) + if verdict.intVal == 0: + localError(c.config, result.info, "concept predicate failed") + of tyUnknown: continue + else: discard + if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): + voidContext = true + n.typ = enforceVoidContext + if i == last and (length == 1 or efWantValue in flags): n.typ = n.sons[i].typ - return + if not isEmptyType(n.typ): n.kind = nkStmtListExpr + elif i != last or voidContext: + discardCheck(c, n.sons[i]) else: - var expr = semExpr(c, n.sons[i], flags) - n.sons[i] = expr - if c.matchedConcept != nil and expr.typ != nil and - (nfFromTemplate notin n.flags or i != last): - case expr.typ.kind - of tyBool: - if expr.kind == nkInfix and - expr[0].kind == nkSym and - expr[0].sym.name.s == "==": - if expr[1].typ.isUnresolvedStatic: - inferConceptStaticParam(c, expr[1], expr[2]) - continue - elif expr[2].typ.isUnresolvedStatic: - inferConceptStaticParam(c, expr[2], expr[1]) - continue - - let verdict = semConstExpr(c, n[i]) - if verdict.intVal == 0: - localError(result.info, "concept predicate failed") - of tyUnknown: continue - else: discard - if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): - voidContext = true - n.typ = enforceVoidContext - if i == last and (length == 1 or efWantValue in flags): - n.typ = n.sons[i].typ - if not isEmptyType(n.typ): n.kind = nkStmtListExpr - elif i != last or voidContext: - discardCheck(c, n.sons[i]) - else: - n.typ = n.sons[i].typ - if not isEmptyType(n.typ): n.kind = nkStmtListExpr - if n.sons[i].kind in LastBlockStmts or - n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags: - for j in countup(i + 1, length - 1): - case n.sons[j].kind - of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, - nkBlockStmt, nkState: discard - else: localError(n.sons[j].info, errStmtInvalidAfterReturn) - else: discard + n.typ = n.sons[i].typ + if not isEmptyType(n.typ): n.kind = nkStmtListExpr + if n.sons[i].kind in LastBlockStmts or + n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and + sfNoReturn in n.sons[i][0].sym.flags: + for j in countup(i + 1, length - 1): + case n.sons[j].kind + of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, + nkBlockStmt, nkState: discard + else: localError(c.config, n.sons[j].info, "unreachable statement after 'return'") + else: discard if result.len == 1 and # concept bodies should be preserved as a stmt list: @@ -1876,15 +1869,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = not (result.comment[0] == '#' and result.comment[1] == '#'): # it is an old-style comment statement: we replace it with 'discard ""': prettybase.replaceComment(result.info) - when false: - # a statement list (s; e) has the type 'e': - if result.kind == nkStmtList and result.len > 0: - var lastStmt = lastSon(result) - if lastStmt.kind != nkNilLit and not implicitlyDiscardable(lastStmt): - result.typ = lastStmt.typ - #localError(lastStmt.info, errGenerated, - # "Last expression must be explicitly returned if it " & - # "is discardable or discarded") proc semStmt(c: PContext, n: PNode): PNode = # now: simply an alias: |