# # # The Nim Compiler # (c) Copyright 2017 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module contains the data structures for the semantic checking phase. import tables import intsets, options, ast, astalgo, msgs, idents, renderer, magicsys, vmdef, modulegraphs, lineinfos, sets, pathutils import ic / ic type TOptionEntry* = object # entries to put on a stack for pragma parsing options*: TOptions defaultCC*: TCallingConvention dynlib*: PLib notes*: TNoteKinds features*: set[Feature] otherPragmas*: PNode # every pragma can be pushed warningAsErrors*: TNoteKinds POptionEntry* = ref TOptionEntry PProcCon* = ref TProcCon TProcCon* {.acyclic.} = object # procedure context; also used for top-level # statements owner*: PSym # the symbol this context belongs to resultSym*: PSym # the result symbol (if we are in a proc) selfSym*: PSym # the 'self' symbol (if available) nestedLoopCounter*: int # whether we are in a loop or not nestedBlockCounter*: int # whether we are in a block or not next*: PProcCon # used for stacking procedure contexts mappingExists*: bool mapping*: TIdTable caseContext*: seq[tuple[n: PNode, idx: int]] localBindStmts*: seq[PNode] TMatchedConcept* = object candidateType*: PType prev*: ptr TMatchedConcept depth*: int TInstantiationPair* = object genericSym*: PSym inst*: PInstantiation TExprFlag* = enum efLValue, efWantIterator, efWantIterable, efInTypeof, efNeedStatic, # Use this in contexts where a static value is mandatory efPreferStatic, # Use this in contexts where a static value could bring more # information, but it's not strictly mandatory. This may become # the default with implicit statics in the future. efPreferNilResult, # Use this if you want a certain result (e.g. static value), # but you don't want to trigger a hard error. For example, # you may be in position to supply a better error message # to the user. efWantStmt, efAllowStmt, efDetermineType, efExplain, efWantValue, efOperand, efNoSemCheck, efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check, efNoUndeclared # Use this if undeclared identifiers should not raise an error during # overload resolution. TExprFlags* = set[TExprFlag] ImportMode* = enum importAll, importSet, importExcept ImportedModule* = object m*: PSym case mode*: ImportMode of importAll: discard of importSet: imported*: IntSet # of PIdent.id of importExcept: exceptSet*: IntSet # of PIdent.id PContext* = ref TContext TContext* = object of TPassContext # a context represents the module # that is currently being compiled enforceVoidContext*: PType # for `if cond: stmt else: foo`, `foo` will be evaluated under # enforceVoidContext != nil voidType*: PType # for typeof(stmt) module*: PSym # the module sym belonging to the context currentScope*: PScope # current scope moduleScope*: PScope # scope for modules imports*: seq[ImportedModule] # scope for all imported symbols topLevelScope*: PScope # scope for all top-level symbols p*: PProcCon # procedure context intTypeCache*: array[-5..32, PType] # cache some common integer types # to avoid type allocations nilTypeCache*: PType matchedConcept*: ptr TMatchedConcept # the current concept being matched friendModules*: seq[PSym] # friend modules; may access private data; # this is used so that generic instantiations # can access private object fields instCounter*: int # to prevent endless instantiations templInstCounter*: ref int # gives every template instantiation a unique id inGenericContext*: int # > 0 if we are in a generic type inStaticContext*: int # > 0 if we are inside a static: block inUnrolledContext*: int # > 0 if we are unrolling a loop compilesContextId*: int # > 0 if we are in a ``compiles`` magic compilesContextIdGenerator*: int inGenericInst*: int # > 0 if we are instantiating a generic converters*: seq[PSym] patterns*: seq[PSym] # sequence of pattern matchers optionStack*: seq[POptionEntry] symMapping*: TIdTable # every gensym'ed symbol needs to be mapped # to some new symbol in a generic instantiation libs*: seq[PLib] # all libs used by this module semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} computeRequiresInit*: proc (c: PContext, t: PType): bool {.nimcall.} hasUnresolvedArgs*: proc (c: PContext, n: PNode): bool semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, filter: TSymKinds, flags: TExprFlags): PNode {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym includedFiles*: IntSet # used to detect recursive include files pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously userPragmas*: TStrTable evalContext*: PEvalContext unknownIdents*: IntSet # ids of all unknown identifiers to prevent # naming it multiple times generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile topStmts*: int # counts the number of encountered top level statements lastGenericIdx*: int # used for the generics stack hloLoopDetector*: int # used to prevent endless loops in the HLO inParallelStmt*: int instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo; op: TTypeAttachedOp; col: int): PSym {.nimcall.} selfName*: PIdent cache*: IdentCache graph*: ModuleGraph signatures*: TStrTable recursiveDep*: string suggestionsMade*: bool isAmbiguous*: bool # little hack features*: set[Feature] inTypeContext*, inConceptDecl*: int unusedImports*: seq[(PSym, TLineInfo)] exportIndirections*: HashSet[(int, int)] # (module.id, symbol.id) importModuleMap*: Table[int, int] # (module.id, module.id) lastTLineInfo*: TLineInfo sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index inUncheckedAssignSection*: int template config*(c: PContext): ConfigRef = c.graph.config proc getIntLitType*(c: PContext; literal: PNode): PType = # we cache some common integer literal types for performance: let value = literal.intVal if value >= low(c.intTypeCache) and value <= high(c.intTypeCache): result = c.intTypeCache[value.int] if result == nil: let ti = getSysType(c.graph, literal.info, tyInt) result = copyType(ti, nextTypeId(c.idgen), ti.owner) result.n = literal c.intTypeCache[value.int] = result else: let ti = getSysType(c.graph, literal.info, tyInt) result = copyType(ti, nextTypeId(c.idgen), ti.owner) result.n = literal proc setIntLitType*(c: PContext; result: PNode) = let i = result.intVal case c.config.target.intSize of 8: result.typ = getIntLitType(c, result) of 4: if i >= low(int32) and i <= high(int32): result.typ = getIntLitType(c, result) else: result.typ = getSysType(c.graph, result.info, tyInt64) of 2: if i >= low(int16) and i <= high(int16): result.typ = getIntLitType(c, result) elif i >= low(int32) and i <= high(int32): result.typ = getSysType(c.graph, result.info, tyInt32) else: result.typ = getSysType(c.graph, result.info, tyInt64) of 1: # 8 bit CPUs are insane ... if i >= low(int8) and i <= high(int8): result.typ = getIntLitType(c, result) elif i >= low(int16) and i <= high(int16): result.typ = getSysType(c.graph, result.info, tyInt16) elif i >= low(int32) and i <= high(int32): result.typ = getSysType(c.graph, result.info, tyInt32) else: result.typ = getSysType(c.graph, result.info, tyInt64) else: internalError(c.config, result.info, "invalid int size") proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s result.inst = inst proc filename*(c: PContext): string = # the module's filename return toFilename(c.config, FileIndex c.module.position) proc scopeDepth*(c: PContext): int {.inline.} = result = if c.currentScope != nil: c.currentScope.depthLevel else: 0 proc getCurrOwner*(c: PContext): PSym = # owner stack (used for initializing the # owner field of syms) # the documentation comment always gets # assigned to the current owner result = c.graph.owners[^1] proc pushOwner*(c: PContext; owner: PSym) = c.graph.owners.add(owner) proc popOwner*(c: PContext) = if c.graph.owners.len > 0: setLen(c.graph.owners, c.graph.owners.len - 1) else: internalError(c.config, "popOwner") proc lastOptionEntry*(c: PContext): POptionEntry = result = c.optionStack[^1] proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next proc put*(p: PProcCon; key, val: PSym) = if not p.mappingExists: initIdTable(p.mapping) p.mappingExists = true #echo "put into table ", key.info p.mapping.idTablePut(key, val) proc get*(p: PProcCon; key: PSym): PSym = if not p.mappingExists: return nil result = PSym(p.mapping.idTableGet(key)) proc getGenSym*(c: PContext; s: PSym): PSym = if sfGenSym notin s.flags: return s var it = c.p while it != nil: result = get(it, s) if result != nil: #echo "got from table ", result.name.s, " ", result.info return result it = it.next result = s proc considerGenSyms*(c: PContext; n: PNode) = if n == nil: discard "can happen for nkFormalParams/nkArgList" elif n.kind == nkSym: let s = getGenSym(c, n.sym) if n.sym != s: n.sym = s else: for i in 0..