summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md6
-rw-r--r--compiler/commands.nim39
-rw-r--r--compiler/nim.nim4
-rw-r--r--compiler/options.nim14
-rw-r--r--compiler/pragmas.nim22
-rw-r--r--compiler/scriptconfig.nim6
-rw-r--r--compiler/semasgn.nim4
-rw-r--r--compiler/semcall.nim2
-rw-r--r--compiler/semdata.nim6
-rw-r--r--compiler/semexprs.nim6
-rw-r--r--compiler/semstmts.nim71
-rw-r--r--compiler/semtypinst.nim4
-rw-r--r--compiler/service.nim12
-rw-r--r--compiler/transf.nim6
-rw-r--r--doc/advopt.txt5
-rw-r--r--doc/manual.rst21
-rw-r--r--nimsuggest/nimsuggest.nim8
-rw-r--r--tests/parallel/tparfind.nim2
18 files changed, 122 insertions, 116 deletions
diff --git a/changelog.md b/changelog.md
index a3332789a..8ac02a388 100644
--- a/changelog.md
+++ b/changelog.md
@@ -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
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 8d73ac90e..dc5d808ec 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -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"):
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 8f3463be9..3fa733303 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -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)
diff --git a/compiler/options.nim b/compiler/options.nim
index 7126d4398..24c9d0f73 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -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
 
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 9e9233fe7..5aa903771 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -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])
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 692a8d896..c533f4cb4 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -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
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index bbd2baf6e..5c7b91f6d 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -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!
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 0fc12f164..f443339f5 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -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?
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index bcc1bba15..8159abf8f 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -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
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 279b6cc57..6ad5d931d 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -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
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 8d7747fb4..94090852f 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -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:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 09434c925..c97c1186e 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -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]
diff --git a/compiler/service.nim b/compiler/service.nim
index ac04b7860..7cdfc112c 100644
--- a/compiler/service.nim
+++ b/compiler/service.nim
@@ -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
 
diff --git a/compiler/transf.nim b/compiler/transf.nim
index f30f8583a..f7ec6c97f 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -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)
diff --git a/doc/advopt.txt b/doc/advopt.txt
index bf7dd7fb4..0d1578f78 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -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
diff --git a/doc/manual.rst b/doc/manual.rst
index f1330d524..636bf796b 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -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)
 
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index 49e61b9a9..b2764e9ee 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -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
diff --git a/tests/parallel/tparfind.nim b/tests/parallel/tparfind.nim
index 9de5012f5..4b3610c67 100644
--- a/tests/parallel/tparfind.nim
+++ b/tests/parallel/tparfind.nim
@@ -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: