summary refs log tree commit diff stats
path: root/compiler/options.nim
blob: 662848053c588daefef0be5f5677051f4ea0ac5b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#
#
#           The Nimrod Compiler
#        (c) Copyright 2011 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

import 
  os, lists, strutils, strtabs
  
const
  hasTinyCBackend* = defined(tinyc)

type                          # please make sure we have under 32 options
                              # (improves code efficiency a lot!)
  TOption* = enum             # **keep binary compatible**
    optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, 
    optOverflowCheck, optNilCheck,
    optNaNCheck, optInfCheck,
    optAssert, optLineDir, optWarns, optHints, 
    optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
    optLineTrace,             # line tracing support (includes stack tracing)
    optEndb,                  # embedded debugger
    optByRef,                 # use pass by ref for objects
                              # (for interfacing with C)
    optProfiler               # profiler turned on
  TOptions* = set[TOption]
  TGlobalOption* = enum 
    gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim, 
    optListCmd, optCompileOnly, optNoLinking, 
    optSafeCode,              # only allow safe code
    optCDebug,                # turn on debugging information
    optGenDynLib,             # generate a dynamic library
    optGenGuiApp,             # generate a GUI application
    optGenScript,             # generate a script file to compile the *.c files
    optGenMapping,            # generate a mapping file
    optRun,                   # run the compiled project
    optSymbolFiles,           # use symbol files for speeding up compilation
    optSkipConfigFile,        # skip the general config file
    optSkipProjConfigFile,    # skip the project's config file
    optNoMain,                # do not generate a "main" proc
    optThreads,               # support for multi-threading
    optStdout,                # output to stdout
    optSuggest,               # ideTools: 'suggest'
    optContext,               # ideTools: 'context'
    optDef,                   # ideTools: 'def'
    optThreadAnalysis         # thread analysis pass

  TGlobalOptions* = set[TGlobalOption]
  TCommands* = enum           # Nimrod's commands
    cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, 
    cmdCompileToEcmaScript, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, 
    cmdGenDepend, cmdDump, 
    cmdCheck,                 # semantic checking for whole project
    cmdParse,                 # parse a single file (for debugging)
    cmdScan,                  # scan a single file (for debugging)
    cmdIdeTools,              # ide tools
    cmdDef,                   # def feature (find definition for IDEs)
    cmdRst2html,              # convert a reStructuredText file to HTML
    cmdRst2tex,               # convert a reStructuredText file to TeX
    cmdInteractive,           # start interactive session
    cmdRun                    # run the project via TCC backend
  TStringSeq* = seq[string]

const 
  ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, 
    optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}

var 
  gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, 
                         optBoundsCheck, optOverflowCheck, optAssert, optWarns, 
                         optHints, optStackTrace, optLineTrace}
  gGlobalOptions*: TGlobalOptions = {optRefcGC, optThreadAnalysis}
  gExitcode*: int8
  searchPaths*: TLinkedList
  outFile*: string = ""
  gIndexFile*: string = ""
  gCmd*: TCommands = cmdNone  # the command
  gVerbosity*: int            # how verbose the compiler is
  gNumberOfProcessors*: int   # number of processors

proc FindFile*(f: string): string

const 
  genSubDir* = "nimcache"
  NimExt* = "nim"
  RodExt* = "rod"
  HtmlExt* = "html"
  TexExt* = "tex"
  IniExt* = "ini"
  DocConfig* = "nimdoc.cfg"
  DocTexConfig* = "nimdoc.tex.cfg"

proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string
proc toGeneratedFile*(path, ext: string): string
  # converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
proc getPrefixDir*(): string
  # gets the application directory

# additional configuration variables:
var 
  gConfigVars*: PStringTable
  libpath*: string = ""
  projectPath*: string = ""
  gKeepComments*: bool = true # whether the parser needs to keep comments
  gImplicitMods*: TStringSeq = @[] # modules that are to be implicitly imported

proc existsConfigVar*(key: string): bool
proc getConfigVar*(key: string): string
proc setConfigVar*(key, val: string)
proc addImplicitMod*(filename: string)
proc binaryStrSearch*(x: openarray[string], y: string): int
# implementation

proc existsConfigVar(key: string): bool = 
  result = hasKey(gConfigVars, key)

proc getConfigVar(key: string): string = 
  result = gConfigVars[key]

proc setConfigVar(key, val: string) = 
  gConfigVars[key] = val

proc getOutFile*(filename, ext: string): string = 
  if options.outFile != "": result = options.outFile
  else: result = changeFileExt(filename, ext)
  
proc addImplicitMod(filename: string) = 
  var length = len(gImplicitMods)
  setlen(gImplicitMods, length + 1)
  gImplicitMods[length] = filename

proc getPrefixDir(): string = 
  result = SplitPath(getAppDir()).head

proc shortenDir(dir: string): string = 
  # returns the interesting part of a dir
  var prefix = getPrefixDir() & dirSep
  if startsWith(dir, prefix): 
    return substr(dir, len(prefix))
  prefix = getCurrentDir() & dirSep
  if startsWith(dir, prefix): 
    return substr(dir, len(prefix))
  prefix = projectPath & dirSep #writeln(output, prefix);
                                #writeln(output, dir);
  if startsWith(dir, prefix): 
    return substr(dir, len(prefix))
  result = dir

proc removeTrailingDirSep*(path: string): string = 
  if (len(path) > 0) and (path[len(path) - 1] == dirSep): 
    result = substr(path, 0, len(path) - 2)
  else: 
    result = path
  
proc toGeneratedFile(path, ext: string): string = 
  var (head, tail) = splitPath(path)
  if len(head) > 0: head = shortenDir(head & dirSep)
  result = joinPath([projectPath, genSubDir, head, changeFileExt(tail, ext)])

proc completeGeneratedFilePath(f: string, createSubDir: bool = true): string = 
  var (head, tail) = splitPath(f)
  if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
  var subdir = joinPath([projectPath, genSubDir, head])
  if createSubDir: 
    try: 
      createDir(subdir)
    except EOS: 
      writeln(stdout, "cannot create directory: " & subdir)
      quit(1)
  result = joinPath(subdir, tail)

iterator iterSearchPath*(): string = 
  var it = PStrEntry(SearchPaths.head)
  while it != nil: 
    yield it.data
    it = PStrEntry(it.Next)  

proc rawFindFile(f: string): string = 
  if ExistsFile(f): 
    result = f
  else: 
    for it in iterSearchPath():
      result = JoinPath(it, f)
      if ExistsFile(result): return
    result = ""

proc FindFile(f: string): string = 
  result = rawFindFile(f)
  if len(result) == 0: result = rawFindFile(toLower(f))
  
proc binaryStrSearch(x: openarray[string], y: string): int = 
  var a = 0
  var b = len(x) - 1
  while a <= b: 
    var mid = (a + b) div 2
    var c = cmpIgnoreCase(x[mid], y)
    if c < 0: 
      a = mid + 1
    elif c > 0: 
      b = mid - 1
    else: 
      return mid
  result = - 1

gConfigVars = newStringTable(modeStyleInsensitive)
class="p">, result, orig, symMap) if sfBorrow notin orig.flags: # We do not want to generate a body for generic borrowed procs. # As body is a sym to the borrowed proc. let resultType = # todo probably refactor it into a function if result.kind == skMacro: sysTypeFromName(c.graph, n.info, "NimNode") elif not isInlineIterator(result.typ): result.typ.returnType else: nil b = semProcBody(c, b, resultType) result.ast[bodyPos] = hloBody(c, b) excl(result.flags, sfForward) trackProc(c, result, result.ast[bodyPos]) dec c.inGenericInst proc fixupInstantiatedSymbols(c: PContext, s: PSym) = for i in 0..<c.generics.len: if c.generics[i].genericSym.id == s.id: var oldPrc = c.generics[i].inst.sym pushProcCon(c, oldPrc) pushOwner(c, oldPrc) pushInfoContext(c.config, oldPrc.info) openScope(c) var n = oldPrc.ast n[bodyPos] = copyTree(getBody(c.graph, s)) instantiateBody(c, n, oldPrc.typ.n, oldPrc, s) closeScope(c) popInfoContext(c.config) popOwner(c) popProcCon(c) proc sideEffectsCheck(c: PContext, s: PSym) = when false: if {sfNoSideEffect, sfSideEffect} * s.flags == {sfNoSideEffect, sfSideEffect}: localError(s.info, errXhasSideEffects, s.name.s) proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, allowMetaTypes = false): PType = internalAssert c.config, header.kind == tyGenericInvocation var cl: TReplTypeVars = TReplTypeVars(symMap: initSymMapping(), localCache: initTypeMapping(), typeMap: LayeredIdTable(), info: info, c: c, allowMetaTypes: allowMetaTypes ) cl.typeMap.topLayer = initTypeMapping() # We must add all generic params in scope, because the generic body # may include tyFromExpr nodes depending on these generic params. # XXX: This looks quite similar to the code in matchUserTypeClass, # perhaps the code can be extracted in a shared function. openScope(c) let genericTyp = header.base for i, genParam in genericBodyParams(genericTyp): var param: PSym template paramSym(kind): untyped = newSym(kind, genParam.sym.name, c.idgen, genericTyp.sym, genParam.sym.info) if genParam.kind == tyStatic: param = paramSym skConst param.ast = header[i+1].n param.typ = header[i+1] else: param = paramSym skType param.typ = makeTypeDesc(c, header[i+1]) # this scope was not created by the user, # unused params shouldn't be reported. param.flags.incl sfUsed addDecl(c, param) result = replaceTypeVarsT(cl, header) closeScope(c) proc referencesAnotherParam(n: PNode, p: PSym): bool = if n.kind == nkSym: return n.sym.kind == skParam and n.sym.owner == p else: for i in 0..<n.safeLen: if referencesAnotherParam(n[i], p): return true return false proc instantiateProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo) = # XXX: Instantiates a generic proc signature, while at the same # time adding the instantiated proc params into the current scope. # This is necessary, because the instantiation process may refer to # these params in situations like this: # proc foo[Container](a: Container, b: a.type.Item): typeof(b.x) # # Alas, doing this here is probably not enough, because another # proc signature could appear in the params: # proc foo[T](a: proc (x: T, b: typeof(x.y)) # # The solution would be to move this logic into semtypinst, but # at this point semtypinst have to become part of sem, because it # will need to use openScope, addDecl, etc. #addDecl(c, prc) pushInfoContext(c.config, info) var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(c, typeMap, info, nil) var result = instCopyType(cl, prc.typ) let originalParams = result.n result.n = originalParams.shallowCopy for i, resulti in paramTypes(result): # twrong_field_caching requires these 'resetIdTable' calls: if i > FirstParamAt: resetIdTable(cl.symMap) resetIdTable(cl.localCache) # take a note of the original type. If't a free type or static parameter # we'll need to keep it unbound for the `fitNode` operation below... var typeToFit = resulti let needsStaticSkipping = resulti.kind == tyFromExpr let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags if resulti.kind == tyFromExpr: resulti.flags.incl tfNonConstExpr result[i] = replaceTypeVarsT(cl, resulti) if needsStaticSkipping: result[i] = result[i].skipTypes({tyStatic}) if needsTypeDescSkipping: result[i] = result[i].skipTypes({tyTypeDesc}) typeToFit = result[i] # ...otherwise, we use the instantiated type in `fitNode` if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and (typeToFit.kind != tyStatic): typeToFit = result[i] internalAssert c.config, originalParams[i].kind == nkSym let oldParam = originalParams[i].sym let param = copySym(oldParam, c.idgen) param.owner = prc param.typ = result[i] # The default value is instantiated and fitted against the final # concrete param type. We avoid calling `replaceTypeVarsN` on the # call head symbol, because this leads to infinite recursion. if oldParam.ast != nil: var def = oldParam.ast.copyTree if def.typ.kind == tyFromExpr: def.typ.flags.incl tfNonConstExpr if not isIntLit(def.typ): def = prepareNode(cl, def) # allow symchoice since node will be fit later # although expectedType should cover it def = semExprWithType(c, def, {efAllowSymChoice}, typeToFit) if def.referencesAnotherParam(getCurrOwner(c)): def.flags.incl nfDefaultRefsParam var converted = indexTypesMatch(c, typeToFit, def.typ, def) if converted == nil: # The default value doesn't match the final instantiated type. # As an example of this, see: # https://github.com/nim-lang/Nim/issues/1201 # We are replacing the default value with an error node in case # the user calls an explicit instantiation of the proc (this is # the only way the default value might be inserted). param.ast = errorNode(c, def) # we know the node is empty, we need the actual type for error message param.ast.typ = def.typ else: param.ast = fitNodePostMatch(c, typeToFit, converted) param.typ = result[i] result.n[i] = newSymNode(param) propagateToOwner(result, result[i]) addDecl(c, param) resetIdTable(cl.symMap) resetIdTable(cl.localCache) cl.isReturnType = true result.setReturnType replaceTypeVarsT(cl, result.returnType) cl.isReturnType = false result.n[0] = originalParams[0].copyTree if result[0] != nil: propagateToOwner(result, result[0]) eraseVoidParams(result) skipIntLiteralParams(result, c.idgen) prc.typ = result popInfoContext(c.config) proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType = # instantiates only the type of a given proc symbol # used by sigmatch for explicit generics # wouldn't be needed if sigmatch could handle complex cases, # examples are in texplicitgenerics # might be buggy, see rest of generateInstance if problems occur let fakeSym = copySym(prc, c.idgen) incl(fakeSym.flags, sfFromGeneric) fakeSym.instantiatedFrom = prc openScope(c) for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt): addDecl(c, s) instantiateProcType(c, pt, fakeSym, info) closeScope(c) result = fakeSym.typ proc fillMixinScope(c: PContext) = var p = c.p while p != nil: for bnd in p.localBindStmts: for n in bnd: addSym(c.currentScope, n.sym) p = p.next proc getLocalPassC(c: PContext, s: PSym): string = when defined(nimsuggest): return "" if s.ast == nil or s.ast.len == 0: return "" result = "" template extractPassc(p: PNode) = if p.kind == nkPragma and p[0][0].ident == c.cache.getIdent"localpassc": return p[0][1].strVal extractPassc(s.ast[0]) #it is set via appendToModule in pragmas (fast access) for n in s.ast: for p in n: extractPassc(p) proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, info: TLineInfo): PSym = ## Generates a new instance of a generic procedure. ## The `pt` parameter is a type-unsafe mapping table used to link generic ## parameters to their concrete types within the generic instance. # no need to instantiate generic templates/macros: internalAssert c.config, fn.kind notin {skMacro, skTemplate} # generates an instantiated proc if c.instCounter > 50: globalError(c.config, info, "generic instantiation too nested") inc c.instCounter defer: dec c.instCounter # careful! we copy the whole AST including the possibly nil body! var n = copyTree(fn.ast) # NOTE: for access of private fields within generics from a different module # we set the friend module: let producer = getModule(fn) c.friendModules.add(producer) let oldMatchedConcept = c.matchedConcept c.matchedConcept = nil let oldScope = c.currentScope while not isTopLevel(c): c.currentScope = c.currentScope.parent result = copySym(fn, c.idgen) incl(result.flags, sfFromGeneric) result.instantiatedFrom = fn if sfGlobal in result.flags and c.config.symbolFiles != disabledSf: let passc = getLocalPassC(c, producer) if passc != "": #pass the local compiler options to the consumer module too extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex)) result.owner = c.module else: result.owner = fn result.ast = n pushOwner(c, result) # mixin scope: openScope(c) fillMixinScope(c) openScope(c) let gp = n[genericParamsPos] if gp.kind != nkGenericParams: # bug #22137 globalError(c.config, info, "generic instantiation too nested") n[namePos] = newSymNode(result) pushInfoContext(c.config, info, fn.detailedInfo) var entry = TInstantiation.new entry.sym = result # we need to compare both the generic types and the concrete types: # generic[void](), generic[int]() # see ttypeor.nim test. var i = 0 newSeq(entry.concreteTypes, fn.typ.paramsLen+gp.len) # let param instantiation know we are in a concept for unresolved statics: c.matchedConcept = oldMatchedConcept for s in instantiateGenericParamList(c, gp, pt): addDecl(c, s) entry.concreteTypes[i] = s.typ inc i c.matchedConcept = nil pushProcCon(c, result) instantiateProcType(c, pt, result, info) for _, param in paramTypes(result.typ): entry.concreteTypes[i] = param inc i #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len if tfTriggersCompileTime in result.typ.flags: incl(result.flags, sfCompileTime) n[genericParamsPos] = c.graph.emptyNode var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId) if oldPrc == nil: # we MUST not add potentially wrong instantiations to the caching mechanism. # This means recursive instantiations behave differently when in # a ``compiles`` context but this is the lesser evil. See # bug #1055 (tevilcompiles). #if c.compilesContextId == 0: entry.compilesId = c.compilesContextId addToGenericProcCache(c, fn, entry) c.generics.add(makeInstPair(fn, entry)) # bug #12985 bug #22913 # TODO: use the context of the declaration of generic functions instead # TODO: consider fixing options as well let otherPragmas = c.optionStack[^1].otherPragmas c.optionStack[^1].otherPragmas = nil if n[pragmasPos].kind != nkEmpty: pragma(c, result, n[pragmasPos], allRoutinePragmas) if isNil(n[bodyPos]): n[bodyPos] = copyTree(getBody(c.graph, fn)) instantiateBody(c, n, fn.typ.n, result, fn) c.optionStack[^1].otherPragmas = otherPragmas sideEffectsCheck(c, result) if result.magic notin {mSlice, mTypeOf}: # 'toOpenArray' is special and it is allowed to return 'openArray': paramsTypeCheck(c, result.typ) #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len else: #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len result = oldPrc popProcCon(c) popInfoContext(c.config) closeScope(c) # close scope for parameters closeScope(c) # close scope for 'mixin' declarations popOwner(c) c.currentScope = oldScope discard c.friendModules.pop() c.matchedConcept = oldMatchedConcept if result.kind == skMethod: finishMethod(c, result) # inform IC of the generic #addGeneric(c.ic, result, entry.concreteTypes)