diff --git a/compiler/commands.nim b/compiler/commands.nim index 87ded61db8..3874ea38fb 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -50,15 +50,16 @@ const "Compiled at $4\n" & "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n" +proc genFeatureDesc[T: enum](t: typedesc[T]): string {.compileTime.} = + var x = "" + for f in low(T)..high(T): + if x.len > 0: x.add "|" + x.add $f + x + const Usage = slurp"../doc/basicopt.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 + AdvancedUsage = slurp"../doc/advopt.txt".replace(" //", " ") % [genFeatureDesc(Feature), genFeatureDesc(LegacyFeature)] proc getCommandLineDesc(conf: ConfigRef): string = result = (HelpMessage % [VersionAsString, platform.OS[conf.target.hostOS].name, @@ -744,6 +745,11 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf.features.incl parseEnum[Feature](arg) except ValueError: localError(conf, info, "unknown experimental feature") + of "legacy": + try: + conf.legacyFeatures.incl parseEnum[LegacyFeature](arg) + except ValueError: + localError(conf, info, "unknown obsolete feature") of "nocppexceptions": expectNoArg(conf, switch, arg, pass, info) incl(conf.globalOptions, optNoCppExceptions) diff --git a/compiler/options.nim b/compiler/options.nim index 6303224a2d..75eec47560 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -135,6 +135,12 @@ type ## which itself requires `nimble install libffi`, see #10150 ## Note: this feature can't be localized with {.push.} + LegacyFeature* = enum + allowSemcheckedAstModification, + ## Allows to modify a NimNode where the type has already been + ## flaged with nfSem. If you actually do this, it will cause + ## bugs. + SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf @@ -196,6 +202,7 @@ type cppDefines*: HashSet[string] # (*) headerFile*: string features*: set[Feature] + legacyFeatures*: set[LegacyFeature] arguments*: string ## the arguments to be passed to the program that ## should be run ideCmd*: IdeCmd @@ -315,7 +322,7 @@ proc newConfigRef*(): ConfigRef = m: initMsgConfig(), evalExpr: "", cppDefines: initHashSet[string](), - headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic, + headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting}, notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1], configVars: newStringTable(modeStyleInsensitive), diff --git a/compiler/vm.nim b/compiler/vm.nim index 26b7ab5a9a..75f6cc1c34 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1392,27 +1392,32 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkNode) let idx = regs[rb].intVal.int var dest = regs[ra].node - if dest.kind notin {nkEmpty..nkNilLit} and idx <% dest.len: - dest.sons[idx] = regs[rc].node - else: + if nfSem in dest.flags and allowSemcheckedAstModification notin c.config.legacyFeatures: + stackTrace(c, tos, pc, "typechecked nodes may not be modified") + elif dest.kind in {nkEmpty..nkNilLit} or idx >=% dest.len: stackTrace(c, tos, pc, formatErrorIndexBound(idx, dest.len-1)) + else: + dest.sons[idx] = regs[rc].node of opcNAdd: decodeBC(rkNode) var u = regs[rb].node - if u.kind notin {nkEmpty..nkNilLit}: - u.add(regs[rc].node) - else: + if nfSem in u.flags and allowSemcheckedAstModification notin c.config.legacyFeatures: + stackTrace(c, tos, pc, "typechecked nodes may not be modified") + elif u.kind in {nkEmpty..nkNilLit}: stackTrace(c, tos, pc, "cannot add to node kind: " & $u.kind) + else: + u.add(regs[rc].node) regs[ra].node = u of opcNAddMultiple: decodeBC(rkNode) let x = regs[rc].node var u = regs[rb].node - if u.kind notin {nkEmpty..nkNilLit}: - # XXX can be optimized: - for i in 0..