.experimental can now be used to enable specific features

This commit is contained in:
Andreas Rumpf
2018-04-24 09:34:20 +02:00
parent 8ce9e43434
commit ee366f1746
18 changed files with 122 additions and 116 deletions

View File

@@ -102,4 +102,10 @@
- Added ``macros.getProjectPath`` and ``ospaths.putEnv`` procs to Nim's virtual
machine.
- The ``deadCodeElim`` option is now always turned on and the switch has no
effect anymore, but is recognized for backwards compatibility.
- ``experimental`` is now a pragma / command line switch that can enable specific
language extensions, it is not an all-or-nothing switch anymore.
### Bugfixes

View File

@@ -46,9 +46,9 @@ type
passCmd2, # second pass over the command line
passPP # preprocessor called processCommand()
proc processCommand*(switch: string, pass: TCmdLinePass)
proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef)
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
config: ConfigRef = nil)
config: ConfigRef)
# implementation
@@ -58,7 +58,13 @@ const
const
Usage = slurp"../doc/basicopt.txt".replace("//", "")
AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "")
FeatureDesc = block:
var x = ""
for f in low(Feature)..high(Feature):
if x.len > 0: x.add "|"
x.add $f
x
AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "") % FeatureDesc
proc getCommandLineDesc(): string =
result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
@@ -276,7 +282,6 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation)
of "implicitstatic": result = contains(gOptions, optImplicitStatic)
of "patterns": result = contains(gOptions, optPatterns)
of "experimental": result = gExperimentalMode
of "excessivestacktrace": result = contains(gGlobalOptions, optExcessiveStackTrace)
else: invalidCmdLineOption(passCmd1, switch, info)
@@ -339,7 +344,7 @@ proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
options.inclDynlibOverride(arg)
proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
config: ConfigRef = nil) =
config: ConfigRef) =
var
theOS: TSystemOS
cpu: TSystemCPU
@@ -694,8 +699,13 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
# only supported for compatibility. Does nothing.
expectArg(switch, arg, pass, info)
of "experimental":
expectNoArg(switch, arg, pass, info)
gExperimentalMode = true
if arg.len == 0:
config.features.incl oldExperimentalFeatures
else:
try:
config.features.incl parseEnum[Feature](arg)
except ValueError:
localError(info, "unknown experimental feature")
of "nocppexceptions":
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optNoCppExceptions)
@@ -706,7 +716,8 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
config.cppDefine(arg)
of "newruntime":
expectNoArg(switch, arg, pass, info)
newDestructors = true
doAssert(config != nil)
incl(config.features, destructor)
defineSymbol("nimNewRuntime")
of "cppcompiletonamespace":
expectNoArg(switch, arg, pass, info)
@@ -716,10 +727,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: invalidCmdLineOption(pass, switch, info)
proc processCommand(switch: string, pass: TCmdLinePass) =
proc processCommand(switch: string, pass: TCmdLinePass; config: ConfigRef) =
var cmd, arg: string
splitSwitch(switch, cmd, arg, pass, gCmdLineInfo)
processSwitch(cmd, arg, pass, gCmdLineInfo)
processSwitch(cmd, arg, pass, gCmdLineInfo, config)
var
@@ -727,19 +738,19 @@ var
# the arguments to be passed to the program that
# should be run
proc processSwitch*(pass: TCmdLinePass; p: OptParser) =
proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =
# hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
# we fix this here
var bracketLe = strutils.find(p.key, '[')
if bracketLe >= 0:
var key = substr(p.key, 0, bracketLe - 1)
var val = substr(p.key, bracketLe + 1) & ':' & p.val
processSwitch(key, val, pass, gCmdLineInfo)
processSwitch(key, val, pass, gCmdLineInfo, config)
else:
processSwitch(p.key, p.val, pass, gCmdLineInfo)
processSwitch(p.key, p.val, pass, gCmdLineInfo, config)
proc processArgument*(pass: TCmdLinePass; p: OptParser;
argsCount: var int): bool =
argsCount: var int; config: ConfigRef): bool =
if argsCount == 0:
# nim filename.nims is the same as "nim e filename.nims":
if p.key.endswith(".nims"):

View File

@@ -42,7 +42,7 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
writeCommandLineUsage()
else:
# Process command line arguments:
processCmdLine(passCmd1, "")
processCmdLine(passCmd1, "", config)
if gProjectName == "-":
gProjectName = "stdinfile"
gProjectFull = "stdinfile"
@@ -71,7 +71,7 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
processCmdLine(passCmd2, "")
processCmdLine(passCmd2, "", config)
if options.command == "":
rawMessage(errNoCommand, command)
mainCommand(newModuleGraph(config), cache)

View File

@@ -103,13 +103,23 @@ type
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
ideHighlight, ideOutline, ideKnown, ideMsg
Feature* = enum ## experimental features
implicitDeref,
dotOperators,
callOperator,
parallel,
destructor
ConfigRef* = ref object ## eventually all global configuration should be moved here
cppDefines*: HashSet[string]
headerFile*: string
features*: set[Feature]
const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
proc newConfigRef*(): ConfigRef =
result = ConfigRef(cppDefines: initSet[string](),
headerFile: "")
headerFile: "", features: {})
proc cppDefine*(c: ConfigRef; define: string) =
c.cppDefines.incl define
@@ -146,8 +156,6 @@ var
gListFullPaths*: bool
gPreciseStack*: bool = false
gNoNimblePath* = false
gExperimentalMode*: bool
newDestructors*: bool
gDynlibOverrideAll*: bool
useNimNamespace*: bool

View File

@@ -680,6 +680,22 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
elif n.kind == nkExprColonExpr:
result.kind = n.kind # pragma(arg) -> pragma: arg
proc processExperimental(c: PContext; n: PNode; s: PSym) =
if not isTopLevel(c):
localError(n.info, "'experimental' pragma only valid as toplevel statement")
if n.kind notin nkPragmaCallKinds or n.len != 2:
c.features.incl oldExperimentalFeatures
else:
n[1] = c.semConstExpr(c, n[1])
case n[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
try:
c.features.incl parseEnum[Feature](n[1].strVal)
except ValueError:
localError(n[1].info, "unknown experimental feature")
else:
localError(n.info, errStringLiteralExpected)
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
validPragmas: TSpecialWords): bool =
var it = n.sons[i]
@@ -993,11 +1009,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
else:
it.sons[1] = c.semExpr(c, it.sons[1])
of wExperimental:
noVal(it)
if isTopLevel(c):
c.module.flags.incl sfExperimental
else:
localError(it.info, "'experimental' pragma only valid as toplevel statement")
processExperimental(c, it, sym)
of wThis:
if it.kind in nkPragmaCallKinds and it.len == 2:
c.selfName = considerQuotedIdent(it[1])

View File

@@ -26,7 +26,7 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) =
setResult(a, result)
proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
config: ConfigRef = nil): PEvalContext =
config: ConfigRef): PEvalContext =
# For Nimble we need to export 'setupVM'.
result = newCtx(module, cache)
result.mode = emRepl
@@ -128,7 +128,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
cbconf getCommand:
setResult(a, options.command)
cbconf switch:
processSwitch(a.getString 0, a.getString 1, passPP, module.info)
processSwitch(a.getString 0, a.getString 1, passPP, module.info, config)
cbconf hintImpl:
processSpecificNote(a.getString 0, wHint, passPP, module.info,
a.getString 1)
@@ -150,7 +150,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
options.cppDefine(config, a.getString(0))
proc runNimScript*(cache: IdentCache; scriptName: string;
freshDefines=true; config: ConfigRef=nil) =
freshDefines=true; config: ConfigRef) =
rawMessage(hintConf, scriptName)
passes.gIncludeFile = includeModule
passes.gImportModule = importModule

View File

@@ -101,7 +101,7 @@ proc newOpCall(op: PSym; x: PNode): PNode =
proc destructorCall(c: PContext; op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ.sons[0])
result.add(newSymNode(op))
if newDestructors:
if destructor in c.features:
result.add genAddr(c, x)
else:
result.add x
@@ -319,7 +319,7 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
## In the semantic pass this is called in strategic places
## to ensure we lift assignment, destructors and moves properly.
## The later 'destroyer' pass depends on it.
if not newDestructors or not hasDestructor(typ): return
if destructor notin c.features or not hasDestructor(typ): return
when false:
# do not produce wrong liftings while we're still instantiating generics:
# now disabled; breaks topttree.nim!

View File

@@ -434,7 +434,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
"Non-matching candidates for " & renderTree(n) & "\n" &
candidates)
result = semResolvedCall(c, n, r)
elif experimentalMode(c) and canDeref(n):
elif implicitDeref in c.features and canDeref(n):
# try to deref the first argument and then try overloading resolution again:
#
# XXX: why is this here?

View File

@@ -130,6 +130,7 @@ type
signatures*: TStrTable
recursiveDep*: string
suggestionsMade*: bool
features*: set[Feature]
inTypeContext*: int
typesWithOps*: seq[(PType, PType)] #\
# We need to instantiate the type bound ops lazily after
@@ -225,7 +226,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext
result.graph = graph
initStrTable(result.signatures)
result.typesWithOps = @[]
result.features = graph.config.features
proc inclSym(sq: var TSymSeq, s: PSym) =
var L = len(sq)
@@ -398,6 +399,3 @@ proc checkMinSonsLen*(n: PNode, length: int) =
proc isTopLevel*(c: PContext): bool {.inline.} =
result = c.currentScope.depthLevel <= 2
proc experimentalMode*(c: PContext): bool {.inline.} =
result = gExperimentalMode or sfExperimental in c.module.flags

View File

@@ -663,7 +663,7 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
matches(c, n, nOrig, result)
if result.state != csMatch:
# try to deref the first argument:
if experimentalMode(c) and canDeref(n):
if implicitDeref in c.features and canDeref(n):
n.sons[1] = n.sons[1].tryDeref
initCandidate(c, result, t)
matches(c, n, nOrig, result)
@@ -1452,7 +1452,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
typeMismatch(n.info, lhs.typ, rhsTyp)
n.sons[1] = fitNode(c, le, rhs, n.info)
if not newDestructors:
if destructor notin c.features:
if tfHasAsgn in lhs.typ.flags and not lhsIsResult and
mode != noOverloadedAsgn:
return overloadedAsgn(c, lhs, n.sons[1])
@@ -1884,7 +1884,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString)
of mParallel:
if not experimentalMode(c):
if parallel notin c.features:
localError(n.info, "use the {.experimental.} pragma to enable 'parallel'")
result = setMs(n, s)
var x = n.lastSon

View File

@@ -385,30 +385,9 @@ proc checkNilable(v: PSym) =
include semasgn
proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
# consider this:
# var
# x = 0
# withOverloadedAssignment = foo()
# y = use(withOverloadedAssignment)
# We need to split this into a statement list with multiple 'var' sections
# in order for this transformation to be correct.
let L = identDefs.len
let value = identDefs[L-1]
if value.typ != nil and tfHasAsgn in value.typ.flags and not newDestructors:
# the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()':
identDefs.sons[L-1] = emptyNode
if result.kind != nkStmtList:
let oldResult = result
oldResult.add identDefs
result = newNodeI(nkStmtList, result.info)
result.add oldResult
else:
let o = copyNode(orig)
o.add identDefs
result.add o
for i in 0 .. L-3:
result.add overloadedAsgn(c, identDefs[i], value)
elif result.kind == nkStmtList:
if result.kind == nkStmtList:
let o = copyNode(orig)
o.add identDefs
result.add o
@@ -1267,9 +1246,6 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
gp = newNodeI(nkGenericParams, n.info)
if n.sons[paramsPos].kind != nkEmpty:
#if n.kind == nkDo and not experimentalMode(c):
# localError(n.sons[paramsPos].info,
# "use the {.experimental.} pragma to enable 'do' with parameters")
semParamList(c, n.sons[paramsPos], gp, s)
# paramsTypeCheck(c, s.typ)
if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty:
@@ -1363,27 +1339,26 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
proc semOverride(c: PContext, s: PSym, n: PNode) =
case s.name.s.normalize
of "destroy", "=destroy":
if newDestructors:
let t = s.typ
var noError = false
if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar:
var obj = t.sons[1].sons[0]
while true:
incl(obj.flags, tfHasAsgn)
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
else: break
if obj.kind in {tyObject, tyDistinct}:
if obj.destructor.isNil:
obj.destructor = s
else:
localError(n.info, errGenerated,
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
noError = true
if not noError and sfSystemModule notin s.owner.flags:
localError(n.info, errGenerated,
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
of "=destroy":
let t = s.typ
var noError = false
if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar:
var obj = t.sons[1].sons[0]
while true:
incl(obj.flags, tfHasAsgn)
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
else: break
if obj.kind in {tyObject, tyDistinct}:
if obj.destructor.isNil:
obj.destructor = s
else:
localError(n.info, errGenerated,
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
noError = true
if not noError and sfSystemModule notin s.owner.flags:
localError(n.info, errGenerated,
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
incl(s.flags, sfUsed)
of "deepcopy", "=deepcopy":
if s.typ.len == 2 and
@@ -1612,9 +1587,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
s.options = gOptions
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if s.name.s[0] in {'.', '('}:
if s.name.s in [".", ".()", ".="] and not experimentalMode(c) and not newDestructors:
if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s)
elif s.name.s == "()" and not experimentalMode(c):
elif s.name.s == "()" and callOperator notin c.features:
message(n.info, warnDeprecated, "overloaded '()' operators are now .experimental; " & s.name.s)
if n.sons[bodyPos].kind != nkEmpty:

View File

@@ -368,7 +368,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
assert newbody.kind in {tyRef, tyPtr}
assert newbody.lastSon.typeInst == nil
newbody.lastSon.typeInst = result
if newDestructors:
if destructor in cl.c.features:
cl.c.typesWithOps.add((newbody, result))
else:
typeBound(cl.c, newbody, result, assignment, cl.info)
@@ -545,7 +545,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: discard
proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) =
if not newDestructors: return
if destructor notin c.features: return
var i = 0
while i < c.typesWithOps.len:
let (newty, oldty) = c.typesWithOps[i]

View File

@@ -26,7 +26,7 @@ var
# in caas mode, the list of defines and options will be given at start-up?
# it's enough to check that the previous compilation command is the same?
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
proc processCmdLine*(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
var p = parseopt.initOptParser(cmd)
var argsCount = 0
while true:
@@ -36,19 +36,19 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
of cmdLongoption, cmdShortOption:
if p.key == " ":
p.key = "-"
if processArgument(pass, p, argsCount): break
if processArgument(pass, p, argsCount, config): break
else:
processSwitch(pass, p)
processSwitch(pass, p, config)
of cmdArgument:
if processArgument(pass, p, argsCount): break
if processArgument(pass, p, argsCount, config): break
if pass == passCmd2:
if optRun notin gGlobalOptions and arguments != "" and options.command.normalize != "run":
rawMessage(errArgsNeedRunOption, [])
proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}) =
proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; config: ConfigRef) =
template execute(cmd) =
curCaasCmd = cmd
processCmdLine(passCmd2, cmd)
processCmdLine(passCmd2, cmd, config)
action(cache)
gErrorCounter = 0

View File

@@ -979,7 +979,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
#result = liftLambdas(prc, result)
when useEffectSystem: trackProc(prc, result)
result = liftLocalsIfRequested(prc, result)
if c.needsDestroyPass and newDestructors:
if c.needsDestroyPass: #and newDestructors:
result = injectDestructorCalls(prc, result)
incl(result.flags, nfTransf)
#if prc.name.s == "testbody":
@@ -996,7 +996,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
when useEffectSystem: trackTopLevelStmt(module, result)
#if n.info ?? "temp.nim":
# echo renderTree(result, {renderIds})
if c.needsDestroyPass and newDestructors:
if c.needsDestroyPass:
result = injectDestructorCalls(module, result)
incl(result.flags, nfTransf)
@@ -1007,6 +1007,6 @@ proc transformExpr*(module: PSym, n: PNode): PNode =
var c = openTransf(module, "")
result = processTransf(c, n, module)
liftDefer(c, result)
if c.needsDestroyPass and newDestructors:
if c.needsDestroyPass:
result = injectDestructorCalls(module, result)
incl(result.flags, nfTransf)

View File

@@ -35,7 +35,7 @@ Advanced options:
--noLinking compile Nim and generated files but do not link
--noMain do not generate a main procedure
--genScript generate a compile script (in the 'nimcache'
subdirectory named 'compile_$project$scriptext')
subdirectory named 'compile_$$project$$scriptext')
--genDeps generate a '.deps' file containing the dependencies
--os:SYMBOL set the target operating system (cross-compilation)
--cpu:SYMBOL set the target processor (cross-compilation)
@@ -88,5 +88,6 @@ Advanced options:
--parallelBuild:0|1|... perform a parallel build
value = number of processors (0 for auto-detect)
--verbosity:0|1|2|3 set Nim's verbosity level (1 is default)
--experimental enable experimental language features
--experimental:$1
enable experimental language feature
-v, --version show detailed version information

View File

@@ -1397,10 +1397,10 @@ dereferencing operations for reference types:
Automatic dereferencing is also performed for the first argument of a routine
call. But currently this feature has to be only enabled
via ``{.experimental.}``:
via ``{.experimental: "implicitDeref".}``:
.. code-block:: nim
{.experimental.}
{.experimental: "implicitDeref".}
proc depth(x: NodeObj): int = ...
@@ -5588,7 +5588,7 @@ dot operators
-------------
**Note**: Dot operators are still experimental and so need to be enabled
via ``{.experimental.}``.
via ``{.experimental: "dotOperators".}``.
Nim offers a special family of dot operators that can be used to
intercept and rewrite proc call and field access attempts, referring
@@ -6885,17 +6885,12 @@ is uncertain (it may be removed any time).
Example:
.. code-block:: nim
{.experimental.}
type
FooId = distinct int
BarId = distinct int
using
foo: FooId
bar: BarId
{.experimental: "parallel".}
proc useUsing(bar, foo) =
echo "bar is of type BarId"
echo "foo is of type FooId"
parallel:
for i in 0..4:
echo "echo in parallel"
Implementation Specific Pragmas
@@ -7917,7 +7912,7 @@ Example:
# Compute PI in an inefficient way
import strutils, math, threadpool
{.experimental.}
{.experimental: "parallel".}
proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1)

View File

@@ -518,7 +518,7 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
close(requests)
close(results)
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
proc processCmdLine*(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
var p = parseopt.initOptParser(cmd)
while true:
parseopt.next(p)
@@ -562,7 +562,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
gRefresh = true
of "maxresults":
suggestMaxResults = parseInt(p.val)
else: processSwitch(pass, p)
else: processSwitch(pass, p, config)
of cmdArgument:
let a = unixToNativePath(p.key)
if dirExists(a) and not fileExists(a.addFileExt("nim")):
@@ -577,7 +577,7 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
if paramCount() == 0:
stdout.writeline(Usage)
else:
processCmdLine(passCmd1, "")
processCmdLine(passCmd1, "", config)
if gMode != mstdin:
msgs.writelnHook = proc (msg: string) = discard
if gProjectName != "":
@@ -616,7 +616,7 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config)
extccomp.initVars()
processCmdLine(passCmd2, "")
processCmdLine(passCmd2, "", config)
let graph = newModuleGraph(config)
graph.suggestMode = true

View File

@@ -4,7 +4,7 @@ discard """
import threadpool, sequtils
{.experimental.}
{.experimental: "parallel".}
proc linearFind(a: openArray[int]; x, offset: int): int =
for i, y in a: