# # # The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## this module does the semantic checking of statements # included from sem.nim const errNoSymbolToBorrowFromFound = "no symbol to borrow from found" errDiscardValueX = "value of type '$1' has to be used (or 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 '$1'" 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'" errRecursiveDependencyIteratorX = "recursion is not supported in iterators: '$1'" errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1" errCannotAssignToGlobal = "cannot assign local to global variable" 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) if n[0].kind != nkEmpty: n[0] = semExprWithType(c, n[0]) let sonType = n[0].typ let sonKind = n[0].kind 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. localError(c.config, n.info, "illegal discard proc, did you mean: " & $n[0] & "()") proc semBreakOrContinue(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1, c.config) if n[0].kind != nkEmpty: if n.kind != nkContinueStmt: var s: PSym = nil case n[0].kind of nkIdent: s = lookUp(c, n[0]) of nkSym: s = n[0].sym 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[0] = x suggestSym(c.graph, x.info, s, c.graph.usageSym) onUse(x.info, s) else: localError(c.config, n.info, errInvalidControlFlowX % s.name.s) else: localError(c.config, n.info, errGenerated, "'continue' cannot have a label") elif c.p.nestedBlockCounter > 0 and n.kind == nkBreakStmt and not c.p.breakInLoop: localError(c.config, n.info, warnUnnamedBreak) elif (c.p.nestedLoopCounter <= 0) and ((c.p.nestedBlockCounter <= 0) or n.kind == nkContinueStmt): localError(c.config, n.info, errInvalidControlFlowX % renderTree(n, {renderNoComments})) proc semAsm(c: PContext, n: PNode): PNode = checkSonsLen(n, 2, c.config) var marker = pragmaAsm(c, n[0]) if marker == '\0': marker = '`' # default marker result = semAsmOrEmit(c, n, marker) proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode = result = n checkSonsLen(n, 2, c.config) openScope(c) n[0] = forceBool(c, semExprWithType(c, n[0], expectedType = getSysType(c.graph, n.info, tyBool))) inc(c.p.nestedLoopCounter) let oldBreakInLoop = c.p.breakInLoop c.p.breakInLoop = true n[1] = semStmt(c, n[1], flags) c.p.breakInLoop = oldBreakInLoop dec(c.p.nestedLoopCounter) closeScope(c) if n[1].typ == c.enforceVoidContext: result.typ = c.enforceVoidContext elif efInTypeof in flags: result.typ = n[1].typ elif implicitlyDiscardable(n[1]): result[1].typ = c.enforceVoidContext proc semProc(c: PContext, n: PNode): PNode proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}; expectedType: PType = nil): PNode = result = semExpr(c, n, flags, expectedType) if result.typ != nil: # XXX tyGenericInst here? if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode = openScope(c) result = semExprBranch(c, n, expectedType = expectedType) closeScope(c) const skipForDiscardable = {nkStmtList, nkStmtListExpr, nkOfBranch, nkElse, nkFinally, nkExceptBranch, nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr, nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref} proc implicitlyDiscardable(n: PNode): bool = # 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): if n.kind != nkNilLit and n.typ != nil: localError(c.config, n.info, errDiscardValueX % n.typ.typeToString) elif n.kind in {nkStmtList, nkStmtListExpr}: n.transitionSonsKind(nkStmtList) for it in n: fixNilType(c, it) n.typ = nil proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) = if c.matchedConcept != nil or efInTypeof in flags: return if result.typ != nil and result.typ.kind notin {tyTyped, tyVoid}: 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})) 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 var typ = commonTypeBegin var expectedType = expectedType var hasElse = false for i in 0.. ```except a, b, c: body``` a.sons[0..0] = move a[0].sons if a.len == 2 and a[0].isInfixAs(): # support ``except Exception as ex: body`` let isImported = semExceptBranchType(a[0][1]) let symbol = newSymG(skLet, a[0][2], c) symbol.typ = if isImported: a[0][1].typ else: a[0][1].typ.toRef(c.idgen) addDecl(c, symbol) # Overwrite symbol in AST with the symbol in the symbol table. a[0][2] = newSymNode(symbol, a[0][2].info) 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 = false for j in 0.. 1: # if number of ``except: body`` blocks is greater than 1 # or more specific exception follows a general except block, it is invalid localError(c.config, a.info, "Only one general except clause is allowed after more specific exceptions") # last child of an nkExcept/nkFinally branch is a statement: if a.kind != nkFinally: a[^1] = semExprBranchScope(c, a[^1], expectedType) typ = commonType(c, typ, a[^1]) if not endsInNoReturn(a[^1]): expectedType = typ else: a[^1] = semExprBranchScope(c, a[^1]) dec last closeScope(c) if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped}: discardCheck(c, n[0], flags) for i in 1.. 1: return getLineInfo(n[1]) of nkAccQuoted, nkPragmaExpr: if len(n) > 0: return getLineInfo(n[0]) else: discard result = n.info let info = getLineInfo(n) if reportToNimsuggest: suggestSym(c.graph, info, result, c.graph.usageSym) proc checkNilable(c: PContext; v: PSym) = if {sfGlobal, sfImportc} * v.flags == {sfGlobal} and v.typ.requiresInit: if v.astdef.isNil: message(c.config, v.info, warnProveInit, v.name.s) elif tfNotNil in v.typ.flags and not v.astdef.typ.isNil and tfNotNil notin v.astdef.typ.flags: message(c.config, v.info, warnProveInit, v.name.s) #include liftdestructors proc addToVarSection(c: PContext; result: var PNode; n: PNode) = if result.kind != nkStmtList: result = makeStmtList(result) result.add n proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = if result.kind == nkStmtList: let o = copyNode(orig) o.add identDefs result.add o else: result.add identDefs proc isDiscardUnderscore(v: PSym): bool = 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 if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "using") for i in 0..= 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: var x = newNodeI(nkCall, key.info) x.add(key) if it.kind in nkPragmaCallKinds and it.len > 1: # pass pragma arguments to the macro too: for i in 1.. 3 and a[^1].kind != nkEmpty: for j in 0.. 3: # var a, b = (1, 2) message(c.config, a.info, warnEachIdentIsTuple) for j in 0.. 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) # this is needed for the evaluation pass, guard checking # and custom pragmas: b = newNodeI(nkIdentDefs, a.info) if importantComments(c.config): # keep documentation information: b.comment = a.comment # postfix not generated here (to generate, get rid of it in transf) if a[j].kind == nkPragmaExpr: var p = newNodeI(nkPragmaExpr, a.info) p.add newSymNode(v) p.add a[j][1] b.add p else: b.add newSymNode(v) # keep type desc for doc generator b.add a[^2] b.add copyTree(def) addToVarSection(c, result, n, b) v.ast = b 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) inc c.inStaticContext var b: PNode for i in 0.. 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) = if n.kind != nkGenericParams: illFormedAst(n, c.config) for i in 0.. 0: x = x.lastSon # we need the 'safeSkipTypes' here because illegally recursive types # can enter at this point, see bug #13763 if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and s.typ.safeSkipTypes(abstractPtrs).kind notin {tyObject, tyEnum}: # type aliases are hard: var t = semTypeNode(c, x, nil) assert t != nil 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.itemId = t.itemId elif t.kind in {tyObject, tyEnum, tyDistinct}: assert s.typ != nil assignType(s.typ, t) s.typ.itemId = t.itemId # same id 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) proc semAllTypeSections(c: PContext; n: PNode): PNode = proc gatherStmts(c: PContext; n: PNode; result: PNode) {.nimcall.} = case n.kind of nkIncludeStmt: for i in 0.. 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) proc swapResult(n: PNode, sRes: PSym, dNode: PNode) = ## Swap nodes that are (skResult) symbols to d(estination)Node. for i in 0.. resultPos and n[resultPos] != nil: if n[resultPos].sym.kind != skResult: localError(c.config, n.info, "incorrect result proc symbol") if n[resultPos].sym.owner != getCurrOwner(c): # re-write result with new ownership, and re-write the proc accordingly let sResSym = n[resultPos].sym genResSym(s) n[resultPos] = newSymNode(s) swapResult(n, sResSym, n[resultPos]) c.p.resultSym = n[resultPos].sym else: genResSym(s) c.p.resultSym = s n.add newSymNode(c.p.resultSym) addParamOrResult(c, c.p.resultSym, owner) 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..= 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: var x = newNodeI(nkCall, key.info) x.add(key) if it.kind in nkPragmaCallKinds and it.len > 1: # pass pragma arguments to the macro too: for i in 1..= 2 and t.returnType == nil if cond: var obj = t.firstParamType.skipTypes({tyVar}) while true: incl(obj.flags, tfHasAsgn) 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: 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: 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, 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.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.firstParamType.skipTypes(abstractInst).elementType.skipTypes(abstractInst) while true: 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: setAttachedOp(c.graph, c.module.position, t, attachedDeepCopy, s) else: localError(c.config, n.info, errGenerated, "cannot bind another 'deepCopy' to: " & typeToString(t)) else: localError(c.config, n.info, errGenerated, "cannot bind 'deepCopy' to: " & typeToString(t)) if t.owner.getModule != s.getModule: localError(c.config, n.info, errGenerated, "type bound operation `" & name & "` can be defined only in the same module with its type (" & t.typeToString() & ")") else: 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, sfOverridden) of "=", "=copy", "=sink": if s.magic == mAsgn: return incl(s.flags, sfUsed) 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.returnType == nil and t.firstParamType.kind == tyVar: var obj = t.firstParamType.elementType while true: incl(obj.flags, tfHasAsgn) 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.skipModifier elif objB.kind in {tyGenericInvocation, tyGenericInst}: objB = objB.genericHead else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): # attach these ops to the canonical tySequence obj = canonType(c, obj) #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj) let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink let ao = getAttachedOp(c.graph, obj, k) if ao == s: discard "forward declared op" elif ao.isNil and tfCheckedForDestructor notin obj.flags: setAttachedOp(c.graph, c.module.position, obj, k, s) else: prevDestructor(c, ao, obj, n.info) if obj.owner.getModule != s.getModule: localError(c.config, n.info, errGenerated, "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") return if sfSystemModule notin s.owner.flags: localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)") 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 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.. 1 for i in 1.. 0: n.comment = proto.ast.comment proto.ast = n # needed for code generation popOwner(c) pushOwner(c, s) if not isAnon: 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 & " 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 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 cursorInProc(c.config, n[bodyPos]): # speed up nimsuggest if s.kind == skMethod: semMethodPrototype(c, s, n) elif isAnon: let gp = n[genericParamsPos] if gp.kind == nkEmpty or (gp.len == 1 and tfRetType in gp[0].typ.flags): # 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.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: localError(c.config, n.info, errGenericLambdaNotAllowed) else: pushProcCon(c, s) if n[genericParamsPos].kind == nkEmpty or s.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) if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ) maybeAddResult(c, s, n) let resultType = if s.kind == skMacro: sysTypeFromName(c.graph, n.info, "NimNode") elif not isInlineIterator(s.typ): s.typ.returnType else: nil # semantic checking also needed with importc in case used in VM s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType)) # unfortunately we cannot skip this step when in 'system.compiles' # context as it may even be evaluated in 'system.compiles': trackProc(c, s, s.ast[bodyPos]) else: 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]) closeScope(c) if s.magic == mNone: fixupInstantiatedSymbols(c, s) if s.kind == skMethod: semMethodPrototype(c, s, n) popProcCon(c) else: if s.kind == skMethod: semMethodPrototype(c, s, n) 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.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) incl(s.flags, sfWasForwarded) elif sfBorrow in s.flags: semBorrow(c, n, s) sideEffectsCheck(c, s) closeScope(c) # close scope for parameters # c.currentScope = oldScope popOwner(c) if n[patternPos].kind != nkEmpty: c.patterns.add(s) if isAnon: n.transitionSonsKind(nkLambda) result.typ = s.typ if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) 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 #if s.ast.isNil: return discard semProcAux(c, s.ast, s.kind, {}) proc semIterator(c: PContext, n: PNode): PNode = # gensym'ed iterator? if n[namePos].kind == nkSym: # gensym'ed iterators might need to become closure iterators: n[namePos].sym.owner = getCurrOwner(c) n[namePos].sym.transitionRoutineSymKind(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[namePos].sym var t = s.typ 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 # -- at least for 0.9.2. if s.typ.callConv == ccClosure: incl(s.typ.flags, tfCapturesEnv) else: s.typ.callConv = ccInline if n[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0: localError(c.config, n.info, errImplOfXexpected % s.name.s) if optOwnedRefs in c.config.globalOptions and result.typ != nil: result.typ = makeVarType(c, result.typ, tyOwned) result.typ.callConv = ccClosure proc semProc(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skProc, procPragmas) proc semFunc(c: PContext, n: PNode): PNode = let validPragmas = if n[namePos].kind != nkEmpty: procPragmas else: lambdaPragmas result = semProcAux(c, n, skFunc, validPragmas) proc semMethod(c: PContext, n: PNode): PNode = 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[namePos].sym # we need to fix the 'auto' return type for the dispatcher here (see tautonotgeneric # test case): let disp = getDispatcher(s) # auto return type? 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") 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[namePos].sym var t = s.typ 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 = 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[namePos].sym var t = s.typ var allUntyped = true var nullary = true for i in 1.. 0 and n[last].kind in {nkPragma, nkCommentStmt, # nkNilLit, nkEmpty}: # dec last for i in 0.. 0: # from templates m = m.lastSon if endsInNoReturn(m): for j in i + 1..