diff options
Diffstat (limited to 'compiler/semstmts.nim')
-rw-r--r-- | compiler/semstmts.nim | 1020 |
1 files changed, 722 insertions, 298 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a2b5a0c92..f5f8fea0c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -40,6 +40,16 @@ const proc implicitlyDiscardable(n: PNode): bool +proc hasEmpty(typ: PType): bool = + if typ.kind in {tySequence, tyArray, tySet}: + result = typ.elementType.kind == tyEmpty + elif typ.kind == tyTuple: + result = false + for s in typ.kids: + result = result or hasEmpty(s) + else: + result = false + proc semDiscard(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1, c.config) @@ -47,7 +57,9 @@ proc semDiscard(c: PContext, n: PNode): PNode = n[0] = semExprWithType(c, n[0]) let sonType = n[0].typ let sonKind = n[0].kind - if isEmptyType(sonType) or sonType.kind in {tyNone, tyTypeDesc} or sonKind == nkTypeOfExpr: + if isEmptyType(sonType) or hasEmpty(sonType) or + sonType.kind in {tyNone, tyTypeDesc} or + sonKind == nkTypeOfExpr: localError(c.config, n.info, errInvalidDiscard) if sonType.kind == tyProc and sonKind notin nkCallKinds: # tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant. @@ -58,7 +70,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = checkSonsLen(n, 1, c.config) if n[0].kind != nkEmpty: if n.kind != nkContinueStmt: - var s: PSym + var s: PSym = nil case n[0].kind of nkIdent: s = lookUp(c, n[0]) of nkSym: s = n[0].sym @@ -105,7 +117,6 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode = result.typ = n[1].typ elif implicitlyDiscardable(n[1]): result[1].typ = c.enforceVoidContext - result.typ = c.enforceVoidContext proc semProc(c: PContext, n: PNode): PNode @@ -121,17 +132,141 @@ proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode closeScope(c) const - skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch, - nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch, + skipForDiscardable = {nkStmtList, nkStmtListExpr, + nkOfBranch, nkElse, nkFinally, nkExceptBranch, nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr, - nkHiddenStdConv, nkHiddenDeref} + nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref} proc implicitlyDiscardable(n: PNode): bool = - var n = n - while n.kind in skipForDiscardable: n = n.lastSon - result = n.kind in nkLastBlockStmts or - (isCallExpr(n) and n[0].kind == nkSym and - sfDiscardable in n[0].sym.flags) + # same traversal as endsInNoReturn + template checkBranch(branch) = + if not implicitlyDiscardable(branch): + return false + + var it = n + # skip these beforehand, no special handling needed + while it.kind in skipForDiscardable and it.len > 0: + it = it.lastSon + + case it.kind + of nkIfExpr, nkIfStmt: + for branch in it: + checkBranch: + if branch.len == 2: + branch[1] + elif branch.len == 1: + branch[0] + else: + raiseAssert "Malformed `if` statement during implicitlyDiscardable" + # all branches are discardable + result = true + of nkCaseStmt: + for i in 1 ..< it.len: + let branch = it[i] + checkBranch: + case branch.kind + of nkOfBranch: + branch[^1] + of nkElifBranch: + branch[1] + of nkElse: + branch[0] + else: + raiseAssert "Malformed `case` statement in implicitlyDiscardable" + # all branches are discardable + result = true + of nkTryStmt: + checkBranch(it[0]) + for i in 1 ..< it.len: + let branch = it[i] + if branch.kind != nkFinally: + checkBranch(branch[^1]) + # all branches are discardable + result = true + of nkCallKinds: + result = it[0].kind == nkSym and {sfDiscardable, sfNoReturn} * it[0].sym.flags != {} + of nkLastBlockStmts: + result = true + else: + result = false + +proc endsInNoReturn(n: PNode, returningNode: var PNode; discardableCheck = false): bool = + ## check if expr ends the block like raising or call of noreturn procs do + result = false # assume it does return + + template checkBranch(branch) = + if not endsInNoReturn(branch, returningNode, discardableCheck): + # proved a branch returns + return false + + var it = n + # skip these beforehand, no special handling needed + let skips = if discardableCheck: skipForDiscardable else: skipForDiscardable-{nkBlockExpr, nkBlockStmt} + while it.kind in skips and it.len > 0: + it = it.lastSon + + case it.kind + of nkIfExpr, nkIfStmt: + var hasElse = false + for branch in it: + checkBranch: + if branch.len == 2: + branch[1] + elif branch.len == 1: + hasElse = true + branch[0] + else: + raiseAssert "Malformed `if` statement during endsInNoReturn" + # none of the branches returned + result = hasElse # Only truly a no-return when it's exhaustive + of nkCaseStmt: + let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc}) + # semCase should already have checked for exhaustiveness in this case + # effectively the same as having an else + var hasElse = caseTyp.shouldCheckCaseCovered() + + # actual noreturn checks + for i in 1 ..< it.len: + let branch = it[i] + checkBranch: + case branch.kind + of nkOfBranch: + branch[^1] + of nkElifBranch: + branch[1] + of nkElse: + hasElse = true + branch[0] + else: + raiseAssert "Malformed `case` statement in endsInNoReturn" + # Can only guarantee a noreturn if there is an else or it's exhaustive + result = hasElse + of nkTryStmt: + checkBranch(it[0]) + var lastIndex = it.len - 1 + if it[lastIndex].kind == nkFinally: + # if finally is noreturn, then the entire statement is noreturn + if endsInNoReturn(it[lastIndex][^1], returningNode, discardableCheck): + return true + dec lastIndex + for i in 1 .. lastIndex: + let branch = it[i] + checkBranch(branch[^1]) + # none of the branches returned + result = true + of nkLastBlockStmts: + result = true + of nkCallKinds: + result = it[0].kind == nkSym and sfNoReturn in it[0].sym.flags + if not result: + returningNode = it + else: + result = false + returningNode = it + +proc endsInNoReturn(n: PNode): bool = + var dummy: PNode = nil + result = endsInNoReturn(n, dummy) proc fixNilType(c: PContext; n: PNode) = if isAtom(n): @@ -149,22 +284,25 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) = if implicitlyDiscardable(result): var n = newNodeI(nkDiscardStmt, result.info, 1) n[0] = result + # notes that it doesn't transform nodes into discard statements elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: if result.typ.kind == tyNone: localError(c.config, result.info, "expression has no type: " & renderTree(result, {renderNoComments})) - var n = result - while n.kind in skipForDiscardable: - if n.kind == nkTryStmt: n = n[0] - else: n = n.lastSon - var s = "expression '" & $n & "' is of type '" & - result.typ.typeToString & "' and has to be used (or discarded)" - if result.info.line != n.info.line or - result.info.fileIndex != n.info.fileIndex: - s.add "; start of expression here: " & c.config$result.info - if result.typ.kind == tyProc: - s.add "; for a function call use ()" - localError(c.config, n.info, s) + else: + # Ignore noreturn procs since they don't have a type + var n = result + if result.endsInNoReturn(n, discardableCheck = true): + return + + var s = "expression '" & $n & "' is of type '" & + result.typ.typeToString & "' and has to be used (or discarded)" + if result.info.line != n.info.line or + result.info.fileIndex != n.info.fileIndex: + s.add "; start of expression here: " & c.config$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; flags: TExprFlags; expectedType: PType = nil): PNode = result = n @@ -178,13 +316,15 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): it[0] = forceBool(c, semExprWithType(c, it[0], expectedType = getSysType(c.graph, n.info, tyBool))) it[1] = semExprBranch(c, it[1], flags, expectedType) typ = commonType(c, typ, it[1]) - expectedType = typ + if not endsInNoReturn(it[1]): + expectedType = typ closeScope(c) elif it.len == 1: hasElse = true it[0] = semExprBranchScope(c, it[0], expectedType) typ = commonType(c, typ, it[0]) - expectedType = typ + if not endsInNoReturn(it[0]): + expectedType = typ else: illFormedAst(it, c.config) if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped} or (not hasElse and efInTypeof notin flags): @@ -210,6 +350,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) isImported = true elif not isException(typ): localError(c.config, typeNode.info, errExprCannotBeRaised) + elif not isDefectOrCatchableError(typ): + message(c.config, a.info, warnBareExcept, "catch a more precise Exception deriving from CatchableError or Defect.") if containsOrIncl(check, typ.id): localError(c.config, typeNode.info, errExceptionAlreadyHandled) @@ -222,8 +364,9 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) var typ = commonTypeBegin var expectedType = expectedType n[0] = semExprBranchScope(c, n[0], expectedType) - typ = commonType(c, typ, n[0].typ) - expectedType = typ + if not endsInNoReturn(n[0]): + typ = commonType(c, typ, n[0].typ) + expectedType = typ var last = n.len - 1 var catchAllExcepts = 0 @@ -251,14 +394,15 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) elif a.len == 1: # count number of ``except: body`` blocks inc catchAllExcepts - + message(c.config, a.info, warnBareExcept, + "The bare except clause is deprecated; use `except CatchableError:` instead") else: # support ``except KeyError, ValueError, ... : body`` if catchAllExcepts > 0: # if ``except: body`` already encountered, # cannot be followed by a ``except KeyError, ... : body`` block inc catchAllExcepts - var isNative, isImported: bool + var isNative, isImported: bool = false for j in 0..<a.len-1: let tmp = semExceptBranchType(a[j]) if tmp: isImported = true @@ -283,7 +427,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) if a.kind != nkFinally: a[^1] = semExprBranchScope(c, a[^1], expectedType) typ = commonType(c, typ, a[^1]) - expectedType = typ + if not endsInNoReturn(a[^1]): + expectedType = typ else: a[^1] = semExprBranchScope(c, a[^1]) dec last @@ -296,7 +441,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) result.typ = c.enforceVoidContext else: if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags) - n[0] = fitNode(c, typ, n[0], n[0].info) + if not endsInNoReturn(n[0]): + n[0] = fitNode(c, typ, n[0], n[0].info) for i in 1..last: var it = n[i] let j = it.len-1 @@ -314,6 +460,8 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result.typ = typ if not floatRangeCheck(result.floatVal, typ): localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)]) + elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring: + discard "keep nkHiddenStdConv for cstring conversions" else: changeType(c, r1, typ, check=true) result = r1 @@ -321,6 +469,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = changeType(c, result, typ, check=false) proc findShadowedVar(c: PContext, v: PSym): PSym = + result = nil for scope in localScopesFrom(c, c.currentScope.parent): let shadowed = strTableGet(scope.symbols, v.name) if shadowed != nil and shadowed.kind in skLocalVars: @@ -333,7 +482,7 @@ proc identWithin(n: PNode, s: PIdent): bool = proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym = if isTopLevel(c): - result = semIdentWithPragma(c, kind, n, {sfExported}) + result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true) incl(result.flags, sfGlobal) #if kind in {skVar, skLet}: # echo "global variable here ", n.info, " ", result.name.s @@ -381,9 +530,11 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = result.add identDefs proc isDiscardUnderscore(v: PSym): bool = - if v.name.s == "_": + if v.name.id == ord(wUnderscore): v.flags.incl(sfGenSym) result = true + else: + result = false proc semUsing(c: PContext; n: PNode): PNode = result = c.graph.emptyNode @@ -408,13 +559,6 @@ proc semUsing(c: PContext; n: PNode): PNode = if a[^1].kind != nkEmpty: localError(c.config, a.info, "'using' sections cannot contain assignments") -proc hasEmpty(typ: PType): bool = - if typ.kind in {tySequence, tyArray, tySet}: - result = typ.lastSon.kind == tyEmpty - elif typ.kind == tyTuple: - for s in typ.sons: - result = result or hasEmpty(s) - proc hasUnresolvedParams(n: PNode; flags: TExprFlags): bool = result = tfUnresolved in n.typ.flags when false: @@ -435,16 +579,16 @@ proc hasUnresolvedParams(n: PNode; flags: TExprFlags): bool = proc makeDeref(n: PNode): PNode = var t = n.typ if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass: - t = t.lastSon + t = t.last t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned}) result = n if t.kind in {tyVar, tyLent}: - result = newNodeIT(nkHiddenDeref, n.info, t[0]) + result = newNodeIT(nkHiddenDeref, n.info, t.elementType) result.add n - t = skipTypes(t[0], {tyGenericInst, tyAlias, tySink, tyOwned}) + t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned}) while t.kind in {tyPtr, tyRef}: var a = result - let baseTyp = t.lastSon + let baseTyp = t.elementType result = newNodeIT(nkHiddenDeref, n.info, baseTyp) result.add a t = skipTypes(baseTyp, {tyGenericInst, tyAlias, tySink, tyOwned}) @@ -455,7 +599,7 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) = let y = considerQuotedIdent(c, n[1]) let obj = x.typ.skipTypes(abstractPtrs) if obj.kind == tyObject and tfPartial in obj.flags: - let field = newSym(skField, getIdent(c.cache, y.s), nextSymId c.idgen, obj.sym, n[1].info) + let field = newSym(skField, getIdent(c.cache, y.s), c.idgen, obj.sym, n[1].info) field.typ = skipIntLit(typ, c.idgen) field.position = obj.n.len obj.n.add newSymNode(field) @@ -478,15 +622,15 @@ proc setVarType(c: PContext; v: PSym, typ: PType) = proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool = # make sure it's not a normal pragma, and calls an identifier # considerQuotedIdent below will fail on non-identifiers - result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds + result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds+{nkDotExpr} if result: # make sure it's not a user pragma - let ident = considerQuotedIdent(c, key) - result = strTableGet(c.userPragmas, ident) == nil + if key.kind != nkDotExpr: + let ident = considerQuotedIdent(c, key) + result = strTableGet(c.userPragmas, ident) == nil if result: # make sure it's not a custom pragma - var amb = false - let sym = searchInScopes(c, ident, amb) + let sym = qualifiedLookUp(c, key, {}) result = sym == nil or sfCustomPragma notin sym.flags proc copyExcept(n: PNode, i: int): PNode = @@ -509,6 +653,8 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode = let it = pragmas[i] let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it + trySuggestPragmas(c, key) + if isPossibleMacroPragma(c, it, key): # we transform ``var p {.m, rest.}`` into ``m(do: var p {.rest.})`` and # let the semantic checker deal with it: @@ -557,16 +703,19 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode = return result +template isLocalSym(sym: PSym): bool = + sym.kind in {skVar, skLet, skParam} and not + ({sfGlobal, sfPure} * sym.flags != {} or + sym.typ.kind == tyTypeDesc or + sfCompileTime in sym.flags) or + sym.kind in {skProc, skFunc, skIterator} and + sfGlobal notin sym.flags + template isLocalVarSym(n: PNode): bool = - n.kind == nkSym and - (n.sym.kind in {skVar, skLet} and not - ({sfGlobal, sfPure} * n.sym.flags != {} or - sfCompileTime in n.sym.flags) or - n.sym.kind in {skProc, skFunc, skIterator} and - sfGlobal notin n.sym.flags - ) - + n.kind == nkSym and isLocalSym(n.sym) + proc usesLocalVar(n: PNode): bool = + result = false for z in 1 ..< n.len: if n[z].isLocalVarSym: return true @@ -578,9 +727,87 @@ proc globalVarInitCheck(c: PContext, n: PNode) = if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n): localError(c.config, n.info, errCannotAssignToGlobal) +const + errTupleUnpackingTupleExpected = "tuple expected for tuple unpacking, but got '$1'" + errTupleUnpackingDifferentLengths = "tuple with $1 elements expected, but got '$2' with $3 elements" + +proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode = + ## expand tuple unpacking assignments into new var/let/const section + ## + ## mirrored with semexprs.makeTupleAssignments + if typ.kind != tyTuple: + localError(c.config, a.info, errTupleUnpackingTupleExpected % + [typeToString(typ, preferDesc)]) + elif a.len-2 != typ.len: + localError(c.config, a.info, errTupleUnpackingDifferentLengths % + [$(a.len-2), typeToString(typ, preferDesc), $typ.len]) + var + tempNode: PNode = nil + lastDef: PNode + let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs + # temporary not needed if not const and RHS is tuple literal + # const breaks with seqs without temporary + let useTemp = def.kind notin {nkPar, nkTupleConstr} or symkind == skConst + if useTemp: + # use same symkind for compatibility with original section + let temp = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info) + temp.typ = typ + temp.flags.incl(sfGenSym) + lastDef = newNodeI(defkind, a.info) + newSons(lastDef, 3) + lastDef[0] = newSymNode(temp) + # NOTE: at the moment this is always ast.emptyNode, see parser.nim + lastDef[1] = a[^2] + lastDef[2] = def + temp.ast = lastDef + addToVarSection(c, origResult, n, lastDef) + tempNode = newSymNode(temp) + result = newNodeI(n.kind, a.info) + for j in 0..<a.len-2: + let name = a[j] + if useTemp and name.kind == nkIdent and name.ident.id == ord(wUnderscore): + # skip _ assignments if we are using a temp as they are already evaluated + continue + if name.kind == nkVarTuple: + # nested tuple + lastDef = newNodeI(nkVarTuple, name.info) + newSons(lastDef, name.len) + for k in 0..<name.len-2: + lastDef[k] = name[k] + else: + lastDef = newNodeI(defkind, name.info) + newSons(lastDef, 3) + lastDef[0] = name + lastDef[^2] = c.graph.emptyNode + if useTemp: + lastDef[^1] = newTupleAccessRaw(tempNode, j) + else: + var val = def[j] + if val.kind == nkExprColonExpr: val = val[1] + lastDef[^1] = val + result.add(lastDef) + proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) + + # transform var x, y = 12 into var x = 12; var y = 12 + # bug #18104; transformation should be finished before templates expansion + # TODO: move warnings for tuple here + var transformed = copyNode(n) + for i in 0..<n.len: + var a = n[i] + if a.kind == nkIdentDefs and a.len > 3 and a[^1].kind != nkEmpty: + for j in 0..<a.len-2: + var b = newNodeI(nkIdentDefs, a.info) + b.add a[j] + b.add a[^2] + b.add copyTree(a[^1]) + transformed.add b + else: + transformed.add a + let n = transformed + for i in 0..<n.len: var a = n[i] if c.config.cmd == cmdIdeTools: suggestStmt(c, a) @@ -593,13 +820,20 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addToVarSection(c, result, b) continue + var hasUserSpecifiedType = false var typ: PType = nil if a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) + hasUserSpecifiedType = true - var typFlags: TTypeAllowedFlags + var typFlags: TTypeAllowedFlags = {} var def: PNode = c.graph.emptyNode + if typ != nil and typ.kind == tyRange and + c.graph.config.isDefined("nimPreviewRangeDefault") and + a[^1].kind == nkEmpty: + a[^1] = firstRange(c.config, typ) + if a[^1].kind != nkEmpty: def = semExprWithType(c, a[^1], {efTypeAllowed}, typ) @@ -620,12 +854,18 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = else: typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen) if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass: - typ = typ.lastSon + typ = typ.last if hasEmpty(typ): localError(c.config, def.info, errCannotInferTypeOfTheLiteral % typ.kind.toHumanStr) elif typ.kind == tyProc and def.kind == nkSym and isGenericRoutine(def.sym.ast): - # tfUnresolved in typ.flags: - localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree) + let owner = typ.owner + let err = + # consistent error message with evaltempl/semMacroExpr + if owner != nil and owner.kind in {skTemplate, skMacro}: + errMissingGenericParamsForTemplate % def.renderTree + else: + errProcHasNoConcreteType % def.renderTree + localError(c.config, def.info, err) when false: # XXX This typing rule is neither documented nor complete enough to # justify it. Instead use the newer 'unowned x' until we figured out @@ -644,43 +884,39 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink}) if a.kind == nkVarTuple: - if tup.kind != tyTuple: - localError(c.config, a.info, errXExpected, "tuple") - elif a.len-2 != tup.len: - localError(c.config, a.info, errWrongNumberOfVariables) - b = newNodeI(nkVarTuple, a.info) - newSons(b, a.len) - # keep type desc for doc generator - # NOTE: at the moment this is always ast.emptyNode, see parser.nim - b[^2] = a[^2] - b[^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(c.config, a.info, warnEachIdentIsTuple) - - for j in 0..<a.len-2: - if a[j].kind == nkDotExpr: - fillPartialObject(c, a[j], - if a.kind != nkVarTuple: typ else: tup[j]) - addToVarSection(c, result, n, a) - continue - var v = semIdentDef(c, a[j], symkind, false) - styleCheckDef(c, v) - onDef(a[j].info, v) - if sfGenSym notin v.flags: - if not isDiscardUnderscore(v): addInterfaceDecl(c, v) - else: - if v.owner == nil: v.owner = c.p.owner - when oKeepVariableNames: - if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) + # generate new section from tuple unpacking and embed it into this one + let assignments = makeVarTupleSection(c, n, a, def, tup, symkind, result) + let resSection = semVarOrLet(c, assignments, symkind) + for resDef in resSection: + addToVarSection(c, result, n, resDef) + else: + if tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and + a.len > 3: + # var a, b = (1, 2) + message(c.config, a.info, warnEachIdentIsTuple) + + for j in 0..<a.len-2: + if a[j].kind == nkDotExpr: + fillPartialObject(c, a[j], typ) + addToVarSection(c, result, n, a) + continue + var v = semIdentDef(c, a[j], symkind, false) + when defined(nimsuggest): + v.hasUserSpecifiedType = hasUserSpecifiedType + styleCheckDef(c, v) + onDef(a[j].info, v) + if sfGenSym notin v.flags: + if not isDiscardUnderscore(v): addInterfaceDecl(c, v) else: - let shadowed = findShadowedVar(c, v) - if shadowed != nil: - shadowed.flags.incl(sfShadowed) - if shadowed.kind == skResult and sfGenSym notin v.flags: - message(c.config, a.info, warnResultShadowed) - if a.kind != nkVarTuple: + if v.owner == nil: v.owner = c.p.owner + when oKeepVariableNames: + if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) + else: + let shadowed = findShadowedVar(c, v) + if shadowed != nil: + shadowed.flags.incl(sfShadowed) + if shadowed.kind == skResult and sfGenSym notin v.flags: + message(c.config, a.info, warnResultShadowed) if def.kind != nkEmpty: if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit) setVarType(c, v, typ) @@ -703,32 +939,26 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = b.add copyTree(def) addToVarSection(c, result, n, b) v.ast = b - else: - 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[j]) - else: v.typ = tup - b[j] = newSymNode(v) - if def.kind == nkEmpty: - let actualType = v.typ.skipTypes({tyGenericInst, tyAlias, - tyUserTypeClassInst}) - if actualType.kind in {tyObject, tyDistinct} and - actualType.requiresInit: - defaultConstructionError(c, v.typ, v.info) - else: - checkNilable(c, v) - # allow let to not be initialised if imported from C: - if v.kind == skLet and sfImportc notin v.flags: - localError(c.config, a.info, errLetNeedsInit) - if sfCompileTime in v.flags: - var x = newNodeI(result.kind, v.info) - x.add result[i] - vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x) - if v.flags * {sfGlobal, sfThread} == {sfGlobal}: - message(c.config, v.info, hintGlobalVar) - if {sfGlobal, sfPure} <= v.flags: - globalVarInitCheck(c, def) - suggestSym(c.graph, v.info, v, c.graph.usageSym) + if def.kind == nkEmpty: + let actualType = v.typ.skipTypes({tyGenericInst, tyAlias, + tyUserTypeClassInst}) + if actualType.kind in {tyObject, tyDistinct} and + actualType.requiresInit: + defaultConstructionError(c, v.typ, v.info) + else: + checkNilable(c, v) + # allow let to not be initialised if imported from C: + if v.kind == skLet and sfImportc notin v.flags and (strictDefs notin c.features or not isLocalSym(v)): + localError(c.config, a.info, errLetNeedsInit) + if sfCompileTime in v.flags: + var x = newNodeI(result.kind, v.info) + x.add result[i] + vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x) + if v.flags * {sfGlobal, sfThread} == {sfGlobal}: + message(c.config, v.info, hintGlobalVar) + if {sfGlobal, sfPure} <= v.flags: + globalVarInitCheck(c, def) + suggestSym(c.graph, v.info, v, c.graph.usageSym) proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) @@ -746,13 +976,16 @@ proc semConst(c: PContext, n: PNode): PNode = addToVarSection(c, result, b) continue + var hasUserSpecifiedType = false var typ: PType = nil if a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) + hasUserSpecifiedType = true - var typFlags: TTypeAllowedFlags + var typFlags: TTypeAllowedFlags = {} # don't evaluate here since the type compatibility check below may add a converter + openScope(c) var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ) if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}: @@ -779,28 +1012,31 @@ proc semConst(c: PContext, n: PNode): PNode = if c.matchedConcept != nil: typFlags.incl taConcept typeAllowedCheck(c, a.info, typ, skConst, typFlags) + closeScope(c) if a.kind == nkVarTuple: - if typ.kind != tyTuple: - localError(c.config, a.info, errXExpected, "tuple") - elif a.len-2 != typ.len: - localError(c.config, a.info, errWrongNumberOfVariables) - b = newNodeI(nkVarTuple, a.info) - newSons(b, a.len) - b[^2] = a[^2] - b[^1] = def - - for j in 0..<a.len-2: - var v = semIdentDef(c, a[j], skConst) - if sfGenSym notin v.flags: addInterfaceDecl(c, v) - elif v.owner == nil: v.owner = getCurrOwner(c) - styleCheckDef(c, v) - onDef(a[j].info, v) - - if a.kind != nkVarTuple: - setVarType(c, v, typ) - when false: - v.ast = def # no need to copy + # generate new section from tuple unpacking and embed it into this one + let assignments = makeVarTupleSection(c, n, a, def, typ, skConst, result) + let resSection = semConst(c, assignments) + for resDef in resSection: + addToVarSection(c, result, n, resDef) + else: + for j in 0..<a.len-2: + var v = semIdentDef(c, a[j], skConst) + when defined(nimsuggest): + v.hasUserSpecifiedType = hasUserSpecifiedType + if sfGenSym notin v.flags: addInterfaceDecl(c, v) + elif v.owner == nil: v.owner = getCurrOwner(c) + styleCheckDef(c, v) + onDef(a[j].info, v) + + var fillSymbol = true + if v.typ != nil: + # symbol already has type and probably value + # don't mutate + fillSymbol = false + else: + setVarType(c, v, typ) b = newNodeI(nkConstDef, a.info) if importantComments(c.config): b.comment = a.comment # postfix not generated here (to generate, get rid of it in transf) @@ -813,13 +1049,9 @@ proc semConst(c: PContext, n: PNode): PNode = b.add newSymNode(v) b.add a[1] b.add copyTree(def) - v.ast = b - else: - setVarType(c, v, typ[j]) - v.ast = if def[j].kind != nkExprColonExpr: def[j] - else: def[j][1] - b[j] = newSymNode(v) - addToVarSection(c, result, n, b) + if fillSymbol: + v.ast = b + addToVarSection(c, result, n, b) dec c.inStaticContext include semfields @@ -846,7 +1078,10 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = if iterAfterVarLent.kind != tyTuple or n.len == 3: if n.len == 3: if n[0].kind == nkVarTuple: - if n[0].len-1 != iterAfterVarLent.len: + if iterAfterVarLent.kind != tyTuple: + return localErrorNode(c, n, n[0].info, errTupleUnpackingTupleExpected % + [typeToString(n[1].typ, preferDesc)]) + elif n[0].len-1 != iterAfterVarLent.len: return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables) for i in 0..<n[0].len-1: @@ -959,12 +1194,12 @@ proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string; if maType == nil: return let headSymbol = selector[0] - var o: TOverloadIter + var o: TOverloadIter = default(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] == maType.typ: + if symx.typ.len == 2 and symx.typ.firstParamType == maType.typ: if match == nil: match = symx else: @@ -981,6 +1216,8 @@ proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string; of skMacro: result = semMacroExpr(c, callExpr, callExpr, match, flags) of skTemplate: result = semTemplateExpr(c, callExpr, match, flags) else: result = nil + else: + result = nil proc handleForLoopMacro(c: PContext; n: PNode; flags: TExprFlags): PNode = result = handleStmtMacro(c, n, n[^2], "ForLoopStmt", flags) @@ -993,8 +1230,8 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode = toResolve.add newIdentNode(getIdent(c.cache, "case"), n.info) toResolve.add n[0] - var errors: CandidateErrors - var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoDiagnostics}, + var errors: CandidateErrors = @[] + var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoUndeclared}, errors, false) if r.state == csMatch: var match = r.calleeSym @@ -1008,7 +1245,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode = of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, flags) of skTemplate: result = semTemplateExpr(c, toExpand, match, flags) else: result = errorNode(c, n[0]) - elif r.state == csNoMatch: + else: result = errorNode(c, n[0]) if result.kind == nkEmpty: localError(c.config, n[0].info, errSelectorMustBeOfCertainTypes) @@ -1026,7 +1263,8 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode = result = n n[^2] = semExprNoDeref(c, n[^2], {efWantIterator}) var call = n[^2] - if call.kind == nkStmtListExpr and isTrivalStmtExpr(call): + + if call.kind == nkStmtListExpr and (isTrivalStmtExpr(call) or (call.lastSon.kind in nkCallKinds and call.lastSon[0].sym.kind == skIterator)): call = call.lastSon n[^2] = call let isCallExpr = call.kind in nkCallKinds @@ -1064,29 +1302,23 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil openScope(c) pushCaseContext(c, n) n[0] = semExprWithType(c, n[0]) - var chckCovered = false var covered: Int128 = toInt128(0) var typ = commonTypeBegin var expectedType = expectedType var hasElse = false let caseTyp = skipTypes(n[0].typ, abstractVar-{tyTypeDesc}) - const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool} + var chckCovered = caseTyp.shouldCheckCaseCovered() case caseTyp.kind - of shouldChckCovered: - chckCovered = true - of tyRange: - if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered: - chckCovered = true - of tyFloat..tyFloat128, tyString, tyCstring, tyError: + of tyFloat..tyFloat128, tyString, tyCstring, tyError, shouldChckCovered, tyRange: discard else: popCaseContext(c) closeScope(c) return handleCaseStmtMacro(c, n, flags) - template invalidOrderOfBranches(n: PNode) = + template invalidOrderOfBranches(n: PNode) = localError(c.config, n.info, "invalid order of case branches") break - + for i in 1..<n.len: setCaseContextIdx(c, i) var x = n[i] @@ -1101,7 +1333,8 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil var last = x.len-1 x[last] = semExprBranchScope(c, x[last], expectedType) typ = commonType(c, typ, x[last]) - expectedType = typ + if not endsInNoReturn(x[last]): + expectedType = typ of nkElifBranch: if hasElse: invalidOrderOfBranches(x) chckCovered = false @@ -1110,13 +1343,15 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil x[0] = forceBool(c, semExprWithType(c, x[0], expectedType = getSysType(c.graph, n.info, tyBool))) x[1] = semExprBranch(c, x[1], expectedType = expectedType) typ = commonType(c, typ, x[1]) - expectedType = typ + if not endsInNoReturn(x[1]): + expectedType = typ closeScope(c) of nkElse: checkSonsLen(x, 1, c.config) x[0] = semExprBranchScope(c, x[0], expectedType) typ = commonType(c, typ, x[0]) - expectedType = typ + if not endsInNoReturn(x[0]): + expectedType = typ if (chckCovered and covered == toCover(c, n[0].typ)) or hasElse: message(c.config, x.info, warnUnreachableElse) hasElse = true @@ -1157,7 +1392,7 @@ proc semRaise(c: PContext, n: PNode): PNode = typ = typ.skipTypes({tyAlias, tyGenericInst, tyOwned}) if typ.kind != tyRef: localError(c.config, n.info, errExprCannotBeRaised) - if typ.len > 0 and not isException(typ.lastSon): + if typ.len > 0 and not isException(typ.elementType): localError(c.config, n.info, "raised object of type $1 does not inherit from Exception" % typeToString(typ)) proc addGenericParamListToScope(c: PContext, n: PNode) = @@ -1173,13 +1408,16 @@ proc typeSectionTypeName(c: PContext; n: PNode): PNode = result = n[0] else: result = n + if result.kind == nkPostfix: + if result.len != 2: illFormedAst(n, c.config) + result = result[1] if result.kind != nkSym: illFormedAst(n, c.config) proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = let typeDef = typeSection[i] checkSonsLen(typeDef, 3, c.config) var name = typeDef[0] - var s: PSym + var s: PSym = nil if name.kind == nkDotExpr and typeDef[2].kind == nkObjectTy: let pkgName = considerQuotedIdent(c, name[0]) let typName = considerQuotedIdent(c, name[1]) @@ -1240,9 +1478,15 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = elif s.owner == nil: s.owner = getCurrOwner(c) if name.kind == nkPragmaExpr: - typeDef[0][0] = newSymNode(s) + if name[0].kind == nkPostfix: + typeDef[0][0][1] = newSymNode(s) + else: + typeDef[0][0] = newSymNode(s) else: - typeDef[0] = newSymNode(s) + if name.kind == nkPostfix: + typeDef[0][1] = newSymNode(s) + else: + typeDef[0] = newSymNode(s) proc typeSectionLeftSidePass(c: PContext, n: PNode) = # process the symbols on the left side for the whole type section, before @@ -1262,7 +1506,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = inc i proc checkCovariantParamsUsages(c: PContext; genericType: PType) = - var body = genericType[^1] + var body = genericType.typeBodyImpl proc traverseSubTypes(c: PContext; t: PType): bool = template error(msg) = localError(c.config, genericType.sym.info, msg) @@ -1279,17 +1523,17 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) = for field in t.n: subresult traverseSubTypes(c, field.typ) of tyArray: - return traverseSubTypes(c, t[1]) + return traverseSubTypes(c, t.elementType) of tyProc: - for subType in t.sons: + for subType in t.signature: if subType != nil: subresult traverseSubTypes(c, subType) if result: error("non-invariant type param used in a proc type: " & $t) of tySequence: - return traverseSubTypes(c, t[0]) + return traverseSubTypes(c, t.elementType) of tyGenericInvocation: - let targetBody = t[0] + let targetBody = t.genericHead for i in 1..<t.len: let param = t[i] if param.kind == tyGenericParam: @@ -1314,13 +1558,13 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) = of tyUserTypeClass, tyUserTypeClassInst: error("non-invariant type parameters are not supported in concepts") of tyTuple: - for fieldType in t.sons: + for fieldType in t.kids: subresult traverseSubTypes(c, fieldType) of tyPtr, tyRef, tyVar, tyLent: - if t.base.kind == tyGenericParam: return true - return traverseSubTypes(c, t.base) + if t.elementType.kind == tyGenericParam: return true + return traverseSubTypes(c, t.elementType) of tyDistinct, tyAlias, tySink, tyOwned: - return traverseSubTypes(c, t.lastSon) + return traverseSubTypes(c, t.skipModifier) of tyGenericInst: internalAssert c.config, false else: @@ -1358,7 +1602,11 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # we fill it out later. For magic generics like 'seq', it won't be filled # so we use tyNone instead of nil to not crash for strange conversions # like: mydata.seq - rawAddSon(s.typ, newTypeS(tyNone, c)) + if s.typ.kind in {tyOpenArray, tyVarargs} and s.typ.len == 1: + # XXX investigate why `tySequence` cannot be added here for now. + discard + else: + rawAddSon(s.typ, newTypeS(tyNone, c)) s.ast = a inc c.inGenericContext var body = semTypeNode(c, a[2], s.typ) @@ -1385,7 +1633,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # possibilities such as instantiating C++ generic types with # garbage collected Nim types. if sfImportc in s.flags: - var body = s.typ.lastSon + var body = s.typ.last if body.kind == tyObject: # erases all declared fields body.n.sons = @[] @@ -1409,22 +1657,30 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # final pass if a[2].kind in nkCallKinds: incl a[2].flags, nfSem # bug #10548 - if sfExportc in s.flags and s.typ.kind == tyAlias: - localError(c.config, name.info, "{.exportc.} not allowed for type aliases") - - if tfBorrowDot in s.typ.flags and s.typ.skipTypes({tyGenericBody}).kind != tyDistinct: - excl s.typ.flags, tfBorrowDot - localError(c.config, name.info, "only a 'distinct' type can borrow `.`") + if sfExportc in s.flags: + if s.typ.kind == tyAlias: + localError(c.config, name.info, "{.exportc.} not allowed for type aliases") + elif s.typ.kind == tyGenericBody: + localError(c.config, name.info, "{.exportc.} not allowed for generic types") + + if tfBorrowDot in s.typ.flags: + let body = s.typ.skipTypes({tyGenericBody}) + if body.kind != tyDistinct: + # flag might be copied from alias/instantiation: + let t = body.skipTypes({tyAlias, tyGenericInst}) + if not (t.kind == tyDistinct and tfBorrowDot in t.flags): + excl s.typ.flags, tfBorrowDot + localError(c.config, name.info, "only a 'distinct' type can borrow `.`") let aa = a[2] if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and aa[0].kind == nkObjectTy: # give anonymous object a dummy symbol: var st = s.typ - if st.kind == tyGenericBody: st = st.lastSon + if st.kind == tyGenericBody: st = st.typeBodyImpl internalAssert c.config, st.kind in {tyPtr, tyRef} - internalAssert c.config, st.lastSon.sym == nil + internalAssert c.config, st.last.sym == nil incl st.flags, tfRefsAnonObj - let objTy = st.lastSon + let objTy = st.last # add flags for `ref object` etc to underlying `object` incl(objTy.flags, oldFlags) # {.inheritable, final.} is already disallowed, but @@ -1432,39 +1688,54 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = if tfInheritable in oldFlags and tfFinal in objTy.flags: excl(objTy.flags, tfFinal) let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"), - nextSymId c.idgen, getCurrOwner(c), s.info) + c.idgen, getCurrOwner(c), s.info) obj.flags.incl sfGeneratedType let symNode = newSymNode(obj) obj.ast = a.shallowCopy case a[0].kind - of nkSym: obj.ast[0] = symNode - of nkPragmaExpr: - obj.ast[0] = a[0].shallowCopy + of nkSym: obj.ast[0] = symNode + of nkPragmaExpr: + obj.ast[0] = a[0].shallowCopy + if a[0][0].kind == nkPostfix: + obj.ast[0][0] = a[0][0].shallowCopy + obj.ast[0][0][1] = symNode + else: obj.ast[0][0] = symNode - obj.ast[0][1] = a[0][1] - else: assert(false) + obj.ast[0][1] = a[0][1] + of nkPostfix: + obj.ast[0] = a[0].shallowCopy + obj.ast[0][1] = symNode + else: assert(false) obj.ast[1] = a[1] obj.ast[2] = a[2][0] if sfPure in s.flags: obj.flags.incl sfPure obj.typ = objTy objTy.sym = obj - -proc checkForMetaFields(c: PContext; n: PNode) = - proc checkMeta(c: PContext; n: PNode; t: PType) = - if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags: - if t.kind == tyBuiltInTypeClass and t.len == 1 and t[0].kind == tyProc: + for sk in c.skipTypes: + discard semTypeNode(c, sk, nil) + c.skipTypes = @[] + +proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) = + proc checkMeta(c: PContext; n: PNode; t: PType; hasError: var bool; parent: PType) = + if t != nil and (t.isMetaType or t.kind == tyNone) and tfGenericTypeParam notin t.flags: + if t.kind == tyBuiltInTypeClass and t.len == 1 and t.elementType.kind == tyProc: localError(c.config, n.info, ("'$1' is not a concrete type; " & "for a callback without parameters use 'proc()'") % t.typeToString) + elif t.kind == tyNone and parent != nil: + # TODO: openarray has the `tfGenericTypeParam` flag & generics + # TODO: handle special cases (sink etc.) and views + localError(c.config, n.info, errTIsNotAConcreteType % parent.typeToString) else: localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString) + hasError = true if n.isNil: return case n.kind of nkRecList, nkRecCase: - for s in n: checkForMetaFields(c, s) + for s in n: checkForMetaFields(c, s, hasError) of nkOfBranch, nkElse: - checkForMetaFields(c, n.lastSon) + checkForMetaFields(c, n.lastSon, hasError) of nkSym: let t = n.sym.typ case t.kind @@ -1472,9 +1743,9 @@ proc checkForMetaFields(c: PContext; n: PNode) = tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink, tyOwned: let start = ord(t.kind in {tyGenericInvocation, tyGenericInst}) for i in start..<t.len: - checkMeta(c, n, t[i]) + checkMeta(c, n, t[i], hasError, t) else: - checkMeta(c, n, t) + checkMeta(c, n, t, hasError, nil) else: internalAssert c.config, false @@ -1509,14 +1780,16 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assert s.typ != nil assignType(s.typ, t) s.typ.itemId = t.itemId # same id - checkConstructedType(c.config, s.info, s.typ) - if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: - checkForMetaFields(c, s.typ.n) - - # fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name: - if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c): - incl(s.flags, sfGenSym) - + var hasError = false + let baseType = s.typ.safeSkipTypes(abstractPtrs) + if baseType.kind in {tyObject, tyTuple} and not baseType.n.isNil and + (x.kind in {nkObjectTy, nkTupleTy} or + (x.kind in {nkRefTy, nkPtrTy} and x.len == 1 and + x[0].kind in {nkObjectTy, nkTupleTy}) + ): + checkForMetaFields(c, baseType.n, hasError) + if not hasError: + checkConstructedType(c.config, s.info, s.typ) #instAllTypeBoundOp(c, n.info) @@ -1590,8 +1863,9 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind) = proc semBorrow(c: PContext, n: PNode, s: PSym) = # search for the correct alias: - var b = searchForBorrowProc(c, c.currentScope.parent, s) - if b != nil: + var (b, state) = searchForBorrowProc(c, c.currentScope.parent, s) + case state + of bsMatch: # store the alias: n[bodyPos] = newSymNode(b) # Carry over the original symbol magic, this is necessary in order to ensure @@ -1600,6 +1874,14 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) = if b.typ != nil and b.typ.len > 0: s.typ.n[0] = b.typ.n[0] s.typ.flags = b.typ.flags + of bsNoDistinct: + localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless") + of bsReturnNotMatch: + localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ.returnType)) + of bsGeneric: + localError(c.config, n.info, "borrow with generic parameter is not supported") + of bsNotSupported: + localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s) else: localError(c.config, n.info, errNoSymbolToBorrowFromFound) @@ -1612,7 +1894,7 @@ proc swapResult(n: PNode, sRes: PSym, dNode: PNode) = proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) = template genResSym(s) = - var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId c.idgen, + var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, getCurrOwner(c), n.info) s.typ = t incl(s.flags, sfUsed) @@ -1637,12 +1919,15 @@ proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) = proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode = # Mirrored with semVarMacroPragma + result = nil var n = prc[pragmasPos] if n == nil or n.kind == nkEmpty: return for i in 0..<n.len: let it = n[i] let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it + trySuggestPragmas(c, key) + if isPossibleMacroPragma(c, it, key): # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and # let the semantic checker deal with it: @@ -1684,7 +1969,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; return result -proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = +proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode = ## used for resolving 'auto' in lambdas based on their callsite var n = n let original = n[namePos].sym @@ -1699,7 +1984,6 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = n[genericParamsPos] = c.graph.emptyNode # for LL we need to avoid wrong aliasing let params = copyTree n.typ.n - n[paramsPos] = params s.typ = n.typ for i in 1..<params.len: if params[i].typ.kind in {tyTypeDesc, tyGenericParam, @@ -1711,8 +1995,8 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = pushOwner(c, s) addParams(c, params, skProc) pushProcCon(c, s) - addResult(c, n, n.typ[0], skProc) - s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ[0])) + addResult(c, n, n.typ.returnType, skProc) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ.returnType)) trackProc(c, s, s.ast[bodyPos]) popProcCon(c) popOwner(c) @@ -1742,8 +2026,8 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) = if s.kind == skMacro: let resultType = sysTypeFromName(c.graph, n.info, "NimNode") addResult(c, n, resultType, s.kind) - elif s.typ[0] != nil and not isInlineIterator(s.typ): - addResult(c, n, s.typ[0], s.kind) + elif s.typ.returnType != nil and not isInlineIterator(s.typ): + addResult(c, n, s.typ.returnType, s.kind) proc canonType(c: PContext, t: PType): PType = if t.kind == tySequence: @@ -1753,7 +2037,7 @@ proc canonType(c: PContext, t: PType): PType = proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) = var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj) - if sfOverriden notin prevOp.flags: + if sfOverridden notin prevOp.flags: msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info) else: msg.add "; previous declaration was here: " & (c.config $ prevOp.info) @@ -1762,30 +2046,79 @@ proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) = proc whereToBindTypeHook(c: PContext; t: PType): PType = result = t while true: - if result.kind in {tyGenericBody, tyGenericInst}: result = result.lastSon + if result.kind in {tyGenericBody, tyGenericInst}: result = result.skipModifier elif result.kind == tyGenericInvocation: result = result[0] else: break if result.kind in {tyObject, tyDistinct, tySequence, tyString}: result = canonType(c, result) -proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = +proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = + let t = s.typ + var noError = false + let cond = t.len == 2 and t.returnType != nil + + if cond: + var obj = t.firstParamType + while true: + incl(obj.flags, tfHasAsgn) + if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier + elif obj.kind == tyGenericInvocation: obj = obj.genericHead + else: break + + var res = t.returnType + while true: + if res.kind in {tyGenericBody, tyGenericInst}: res = res.skipModifier + elif res.kind == tyGenericInvocation: res = res.genericHead + else: break + + if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, res): + obj = canonType(c, obj) + let ao = getAttachedOp(c.graph, obj, op) + if ao == s: + discard "forward declared destructor" + elif ao.isNil and tfCheckedForDestructor notin obj.flags: + setAttachedOp(c.graph, c.module.position, obj, op, s) + else: + prevDestructor(c, ao, obj, n.info) + noError = true + if obj.owner.getModule != s.getModule: + localError(c.config, n.info, errGenerated, + "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") + + if not noError and sfSystemModule notin s.owner.flags: + localError(c.config, n.info, errGenerated, + "signature for '=dup' must be proc[T: object](x: T): T") + + incl(s.flags, sfUsed) + incl(s.flags, sfOverridden) + +proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) = let t = s.typ var noError = false - let cond = if op == attachedDestructor: - t.len == 2 and t[0] == nil and t[1].kind == tyVar - elif op == attachedTrace: - t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer + let cond = case op + of attachedWasMoved: + t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar + of attachedTrace: + t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar and t[2].kind == tyPointer + of attachedDestructor: + if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: + t.len == 2 and t.returnType == nil + else: + t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar else: - t.len >= 2 and t[0] == nil + t.len >= 2 and t.returnType == nil if cond: - var obj = t[1].skipTypes({tyVar}) + var obj = t.firstParamType.skipTypes({tyVar}) while true: incl(obj.flags, tfHasAsgn) - if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon - elif obj.kind == tyGenericInvocation: obj = obj[0] + if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier + elif obj.kind == tyGenericInvocation: obj = obj.genericHead else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: + if (not suppressVarDestructorWarning) and op == attachedDestructor and t.firstParamType.kind == tyVar and + c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: + message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter") obj = canonType(c, obj) let ao = getAttachedOp(c.graph, obj, op) if ao == s: @@ -1799,30 +2132,43 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = localError(c.config, n.info, errGenerated, "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") if not noError and sfSystemModule notin s.owner.flags: - if op == attachedTrace: + case op + of attachedTrace: localError(c.config, n.info, errGenerated, "signature for '=trace' must be proc[T: object](x: var T; env: pointer)") + of attachedDestructor: + if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: + localError(c.config, n.info, errGenerated, + "signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)") + else: + localError(c.config, n.info, errGenerated, + "signature for '=destroy' must be proc[T: object](x: var T)") else: localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") incl(s.flags, sfUsed) - incl(s.flags, sfOverriden) + incl(s.flags, sfOverridden) proc semOverride(c: PContext, s: PSym, n: PNode) = let name = s.name.s.normalize case name of "=destroy": bindTypeHook(c, s, n, attachedDestructor) + if s.ast != nil: + if s.ast[pragmasPos].kind == nkEmpty: + s.ast[pragmasPos] = newNodeI(nkPragma, s.info) + s.ast[pragmasPos].add newTree(nkExprColonExpr, + newIdentNode(c.cache.getIdent("raises"), s.info), newNodeI(nkBracket, s.info)) of "deepcopy", "=deepcopy": if s.typ.len == 2 and - s.typ[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and - sameType(s.typ[1], s.typ[0]): + s.typ.firstParamType.skipTypes(abstractInst).kind in {tyRef, tyPtr} and + sameType(s.typ.firstParamType, s.typ.returnType): # Note: we store the deepCopy in the base of the pointer to mitigate # the problem that pointers are structural types: - var t = s.typ[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst) + var t = s.typ.firstParamType.skipTypes(abstractInst).elementType.skipTypes(abstractInst) while true: - if t.kind == tyGenericBody: t = t.lastSon - elif t.kind == tyGenericInvocation: t = t[0] + if t.kind == tyGenericBody: t = t.typeBodyImpl + elif t.kind == tyGenericInvocation: t = t.genericHead else: break if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}: if getAttachedOp(c.graph, t, attachedDeepCopy).isNil: @@ -1842,26 +2188,26 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = localError(c.config, n.info, errGenerated, "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") incl(s.flags, sfUsed) - incl(s.flags, sfOverriden) + incl(s.flags, sfOverridden) of "=", "=copy", "=sink": if s.magic == mAsgn: return incl(s.flags, sfUsed) - incl(s.flags, sfOverriden) + incl(s.flags, sfOverridden) if name == "=": message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead") let t = s.typ - if t.len == 3 and t[0] == nil and t[1].kind == tyVar: - var obj = t[1][0] + if t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar: + var obj = t.firstParamType.elementType while true: incl(obj.flags, tfHasAsgn) - if obj.kind == tyGenericBody: obj = obj.lastSon - elif obj.kind == tyGenericInvocation: obj = obj[0] + if obj.kind == tyGenericBody: obj = obj.skipModifier + elif obj.kind == tyGenericInvocation: obj = obj.genericHead else: break var objB = t[2] while true: - if objB.kind == tyGenericBody: objB = objB.lastSon + if objB.kind == tyGenericBody: objB = objB.skipModifier elif objB.kind in {tyGenericInvocation, tyGenericInst}: - objB = objB[0] + objB = objB.genericHead else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): # attach these ops to the canonical tySequence @@ -1886,12 +2232,19 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = of "=trace": if s.magic != mTrace: bindTypeHook(c, s, n, attachedTrace) + of "=wasmoved": + if s.magic != mWasMoved: + bindTypeHook(c, s, n, attachedWasMoved) + of "=dup": + if s.magic != mDup: + bindDupHook(c, s, n, attachedDup) else: - if sfOverriden in s.flags: + if sfOverridden in s.flags: localError(c.config, n.info, errGenerated, "'destroy' or 'deepCopy' expected for 'override'") proc cursorInProcAux(conf: ConfigRef; n: PNode): bool = + result = false if inCheckpoint(n.info, conf.m.trackPos) != cpNone: return true for i in 0..<n.safeLen: if cursorInProcAux(conf, n[i]): return true @@ -1899,8 +2252,11 @@ proc cursorInProcAux(conf: ConfigRef; n: PNode): bool = proc cursorInProc(conf: ConfigRef; n: PNode): bool = if n.info.fileIndex == conf.m.trackPos.fileIndex: result = cursorInProcAux(conf, n) + else: + result = false proc hasObjParam(s: PSym): bool = + result = false var t = s.typ for col in 1..<t.len: if skipTypes(t[col], skipPtrs).kind == tyObject: @@ -1910,6 +2266,55 @@ proc finishMethod(c: PContext, s: PSym) = if hasObjParam(s): methodDef(c.graph, c.idgen, s) +proc semCppMember(c: PContext; s: PSym; n: PNode) = + if sfImportc notin s.flags: + let isVirtual = sfVirtual in s.flags + let isCtor = sfConstructor in s.flags + let pragmaName = if isVirtual: "virtual" elif isCtor: "constructor" else: "member" + if c.config.backend == backendCpp: + if s.typ.len < 2 and not isCtor: + localError(c.config, n.info, pragmaName & " must have at least one parameter") + for son in s.typ.signature: + if son!=nil and son.isMetaType: + localError(c.config, n.info, pragmaName & " unsupported for generic routine") + var typ: PType + if isCtor: + typ = s.typ.returnType + if typ == nil or typ.kind != tyObject: + localError(c.config, n.info, "constructor must return an object") + if sfImportc in typ.sym.flags: + localError(c.config, n.info, "constructor in an imported type needs importcpp pragma") + else: + typ = s.typ.firstParamType + if typ.kind == tyPtr and not isCtor: + typ = typ.elementType + if typ.kind != tyObject: + localError(c.config, n.info, pragmaName & " must be either ptr to object or object type.") + if typ.owner.id == s.owner.id and c.module.id == s.owner.id: + c.graph.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s + else: + localError(c.config, n.info, + pragmaName & " procs must be defined in the same scope as the type they are virtual for and it must be a top level scope") + else: + localError(c.config, n.info, pragmaName & " procs are only supported in C++") + else: + var typ = s.typ.returnType + if typ != nil and typ.kind == tyObject and typ.itemId notin c.graph.initializersPerType: + var initializerCall = newTree(nkCall, newSymNode(s)) + var isInitializer = n[paramsPos].len > 1 + for i in 1..<n[paramsPos].len: + let p = n[paramsPos][i] + let val = p[^1] + if val.kind == nkEmpty: + isInitializer = false + break + var j = 0 + while p[j].sym.kind == skParam: + initializerCall.add val + inc j + if isInitializer: + c.graph.initializersPerType[typ.itemId] = initializerCall + proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = if s.isGenericRoutine: let tt = s.typ @@ -1919,7 +2324,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = for col in 1..<tt.len: let t = tt[col] if t != nil and t.kind == tyGenericInvocation: - var x = skipTypes(t[0], {tyVar, tyLent, tyPtr, tyRef, tyGenericInst, + var x = skipTypes(t.genericHead, {tyVar, tyLent, tyPtr, tyRef, tyGenericInst, tyGenericInvocation, tyGenericBody, tyAlias, tySink, tyOwned}) if x.kind == tyObject and t.len-1 == n[genericParamsPos].len: @@ -1944,20 +2349,25 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, result = n checkMinSonsLen(n, bodyPos + 1, c.config) - let isAnon = n[namePos].kind == nkEmpty + let + isAnon = n[namePos].kind == nkEmpty + isHighlight = c.config.ideCmd == ideHighlight var s: PSym case n[namePos].kind of nkEmpty: - s = newSym(kind, c.cache.idAnon, nextSymId c.idgen, c.getCurrOwner, n.info) + s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info) s.flags.incl sfUsed n[namePos] = newSymNode(s) of nkSym: s = n[namePos].sym s.owner = c.getCurrOwner else: - s = semIdentDef(c, n[namePos], kind) + # Highlighting needs to be done early so the position for + # name isn't changed (see taccent_highlight). We don't want to check if this is the + # defintion yet since we are missing some info (comments, side effects) + s = semIdentDef(c, n[namePos], kind, reportToNimsuggest=isHighlight) n[namePos] = newSymNode(s) when false: # disable for now @@ -1972,6 +2382,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, s.ast = n s.options = c.config.options #s.scope = c.currentScope + if s.kind in {skMacro, skTemplate}: + # push noalias flag at first to prevent unwanted recursive calls: + incl(s.flags, sfNoalias) # before compiling the proc params & body, set as current the scope # where the proc was declared @@ -2112,7 +2525,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, pushOwner(c, s) if not isAnon: - if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) + if sfOverridden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) elif s.name.s[0] in {'.', '('}: if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}: localError(c.config, n.info, "the overloaded " & s.name.s & @@ -2124,6 +2537,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if sfBorrow in s.flags and c.config.cmd notin cmdDocLike: result[bodyPos] = c.graph.emptyNode + if sfCppMember * s.flags != {} and sfWasForwarded notin s.flags: + semCppMember(c, s, n) + if n[bodyPos].kind != nkEmpty and sfError notin s.flags: # for DLL generation we allow sfImportc to have a body, for use in VM if c.config.ideCmd in {ideSug, ideCon} and s.kind notin {skMacro, skTemplate} and not @@ -2136,8 +2552,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # absolutely no generics (empty) or a single generic return type are # allowed, everything else, including a nullary generic is an error. pushProcCon(c, s) - addResult(c, n, s.typ[0], skProc) - s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], s.typ[0])) + addResult(c, n, s.typ.returnType, skProc) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], s.typ.returnType)) trackProc(c, s, s.ast[bodyPos]) popProcCon(c) elif efOperand notin flags: @@ -2148,13 +2564,12 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # Macros and Templates can have generic parameters, but they are only # used for overload resolution (there is no instantiation of the symbol) if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ) - maybeAddResult(c, s, n) - let resultType = + let resultType = if s.kind == skMacro: sysTypeFromName(c.graph, n.info, "NimNode") elif not isInlineIterator(s.typ): - s.typ[0] + s.typ.returnType else: nil # semantic checking also needed with importc in case used in VM @@ -2163,8 +2578,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # context as it may even be evaluated in 'system.compiles': trackProc(c, s, s.ast[bodyPos]) else: - if (s.typ[0] != nil and s.kind != skIterator): - addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, s, n.info)) + if (s.typ.returnType != nil and s.kind != skIterator): + addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), c.idgen, s, n.info)) openScope(c) n[bodyPos] = semGenericStmt(c, n[bodyPos]) @@ -2178,8 +2593,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if hasProto: localError(c.config, n.info, errImplOfXexpected % proto.name.s) if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone: # this is a forward declaration and we're building the prototype - if s.kind in {skProc, skFunc} and s.typ[0] != nil and s.typ[0].kind == tyUntyped: - # `auto` is represented as `tyUntyped` at this point in compilation. + if s.kind in {skProc, skFunc} and s.typ.returnType != nil and s.typ.returnType.kind == tyAnything: localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations") incl(s.flags, sfForward) @@ -2200,6 +2614,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, elif isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure: localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid") + # Prevent double highlights. We already highlighted before. + # When not highlighting we still need to allow for suggestions though + if not isHighlight: + suggestSym(c.graph, s.info, s, c.graph.usageSym) + proc determineType(c: PContext, s: PSym) = if s.typ != nil: return #if s.magic != mNone: return @@ -2219,7 +2638,7 @@ proc semIterator(c: PContext, n: PNode): PNode = if result.kind != n.kind: return var s = result[namePos].sym var t = s.typ - if t[0] == nil and s.typ.callConv != ccClosure: + if t.returnType == nil and s.typ.callConv != ccClosure: localError(c.config, n.info, "iterator needs a return type") # iterators are either 'inline' or 'closure'; for backwards compatibility, # we require first class iterators to be marked with 'closure' explicitly @@ -2256,16 +2675,15 @@ proc semMethod(c: PContext, n: PNode): PNode = # test case): let disp = getDispatcher(s) # auto return type? - if disp != nil and disp.typ[0] != nil and disp.typ[0].kind == tyUntyped: - let ret = s.typ[0] - disp.typ[0] = ret + if disp != nil and disp.typ.returnType != nil and disp.typ.returnType.kind == tyUntyped: + let ret = s.typ.returnType + disp.typ.setReturnType ret if disp.ast[resultPos].kind == nkSym: if isEmptyType(ret): disp.ast[resultPos] = c.graph.emptyNode else: disp.ast[resultPos].sym.typ = ret proc semConverterDef(c: PContext, n: PNode): PNode = 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 @@ -2275,12 +2693,11 @@ proc semConverterDef(c: PContext, n: PNode): PNode = if result.kind != nkConverterDef: return var s = result[namePos].sym var t = s.typ - if t[0] == nil: localError(c.config, n.info, errXNeedsReturnType % "converter") + if t.returnType == nil: localError(c.config, n.info, errXNeedsReturnType % "converter") if t.len != 2: localError(c.config, n.info, "a converter takes exactly one argument") addConverterDef(c, LazySym(sym: s)) proc semMacroDef(c: PContext, n: PNode): PNode = - checkSonsLen(n, bodyPos + 1, c.config) result = semProcAux(c, n, skMacro, macroPragmas) # macros can transform macros to nothing: if namePos >= result.safeLen: return result @@ -2291,10 +2708,16 @@ proc semMacroDef(c: PContext, n: PNode): PNode = var s = result[namePos].sym var t = s.typ var allUntyped = true + var nullary = true for i in 1..<t.n.len: let param = t.n[i].sym if param.typ.kind != tyUntyped: allUntyped = false + # no default value, parameters required in call + if param.ast == nil: nullary = false if allUntyped: incl(s.flags, sfAllUntyped) + if nullary and n[genericParamsPos].kind == nkEmpty: + # macro can be called with alias syntax, remove pushed noalias flag + excl(s.flags, sfNoalias) if n[bodyPos].kind == nkEmpty: localError(c.config, n.info, errImplOfXexpected % s.name.s) @@ -2312,20 +2735,23 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) = proc evalInclude(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) result.add n + template checkAs(it: PNode) = + if it.kind == nkInfix and it.len == 3: + let op = it[0].getPIdent + if op != nil and op.id == ord(wAs): + localError(c.config, it.info, "Cannot use '" & it[0].renderTree & "' in 'include'.") for i in 0..<n.len: - var imp: PNode let it = n[i] - if it.kind == nkInfix and it.len == 3 and it[0].ident.s != "/": - localError(c.config, it.info, "Cannot use '" & it[0].ident.s & "' in 'include'.") - if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket: - let sep = it[0] - let dir = it[1] - imp = newNodeI(nkInfix, it.info) - imp.add sep - imp.add dir - imp.add sep # dummy entry, replaced in the loop - for x in it[2]: - imp[2] = x + checkAs(it) + if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket: + let lastPos = it.len - 1 + var imp = copyNode(it) + newSons(imp, it.len) + for i in 0 ..< lastPos: imp[i] = it[i] + imp[lastPos] = imp[0] # dummy entry, replaced in the loop + for x in it[lastPos]: + checkAs(x) + imp[lastPos] = x incMod(c, n, imp, result) else: incMod(c, n, it, result) @@ -2389,8 +2815,11 @@ proc usesResult(n: PNode): bool = elif n.kind == nkReturnStmt: result = true else: + result = false for c in n: if usesResult(c): return true + else: + result = false proc inferConceptStaticParam(c: PContext, inferred, n: PNode) = var typ = inferred.typ @@ -2433,7 +2862,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = let verdict = semConstExpr(c, n[i]) if verdict == nil or verdict.kind != nkIntLit or verdict.intVal == 0: localError(c.config, result.info, "concept predicate failed") - of tyUnknown: continue + of tyFromExpr: continue else: discard if n[i].typ == c.enforceVoidContext: #or usesResult(n[i]): voidContext = true @@ -2446,9 +2875,10 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = else: n.typ = n[i].typ if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr) - if n[i].kind in nkLastBlockStmts or - n[i].kind in nkCallKinds and n[i][0].kind == nkSym and - sfNoReturn in n[i][0].sym.flags: + var m = n[i] + while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates + m = m.lastSon + if endsInNoReturn(m): for j in i + 1..<n.len: case n[j].kind of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkState: discard @@ -2464,12 +2894,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = result[0].kind != nkDefer: result = result[0] - when defined(nimfix): - if result.kind == nkCommentStmt and not result.comment.isNil and - not (result.comment[0] == '#' and result.comment[1] == '#'): - # it is an old-style comment statement: we replace it with 'discard ""': - prettybase.replaceComment(result.info) - proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode = if efInTypeof notin flags: result = semExprNoType(c, n) |