From a96ada7963d67d2dbda324714e41ca87535a1a14 Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Sat, 22 Nov 2025 15:33:53 -0500 Subject: [PATCH 1/5] init --- compiler/commands.nim | 4 ++++ compiler/nimconf.nim | 49 +++++++++++++++++++++++++++++++++++-------- compiler/options.nim | 1 + 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 415fe6b352f7b..94bf5105b4f18 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -631,6 +631,10 @@ proc pathRelativeToConfig(arg: string, pass: TCmdLinePass, conf: ConfigRef): str proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = + if conf.skipParentDetectionMode: + if switch.normalize == "skipparentcfg": + processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info) + return var key = "" var val = "" case switch.normalize diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 5d31dea57f7e7..4d80b3e6e5f7f 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -128,8 +128,9 @@ proc parseDirective(L: var Lexer, tok: var Token; config: ConfigRef; condStack: of wEnd: doEnd(L, tok, condStack) of wWrite: ppGetTok(L, tok) - msgs.msgWriteln(config, strtabs.`%`($tok, config.configVars, - {useEnvironment, useKey})) + if not config.skipParentDetectionMode: + msgs.msgWriteln(config, strtabs.`%`($tok, config.configVars, + {useEnvironment, useKey})) ppGetTok(L, tok) else: case tok.ident.s.normalize @@ -137,19 +138,22 @@ proc parseDirective(L: var Lexer, tok: var Token; config: ConfigRef; condStack: ppGetTok(L, tok) var key = $tok ppGetTok(L, tok) - os.putEnv(key, $tok) + if not config.skipParentDetectionMode: + os.putEnv(key, $tok) ppGetTok(L, tok) of "prependenv": ppGetTok(L, tok) var key = $tok ppGetTok(L, tok) - os.putEnv(key, $tok & os.getEnv(key)) + if not config.skipParentDetectionMode: + os.putEnv(key, $tok & os.getEnv(key)) ppGetTok(L, tok) of "appendenv": ppGetTok(L, tok) var key = $tok ppGetTok(L, tok) - os.putEnv(key, os.getEnv(key) & $tok) + if not config.skipParentDetectionMode: + os.putEnv(key, os.getEnv(key) & $tok) ppGetTok(L, tok) else: lexMessage(L, errGenerated, "invalid directive: '$1'" % $tok) @@ -244,6 +248,21 @@ proc getSystemConfigPath*(conf: ConfigRef; filename: RelativeFile): AbsoluteFile if not fileExists(result): result = p / RelativeDir"etc/nim" / filename if not fileExists(result): result = AbsoluteDir"/etc/nim" / filename +proc configEnablesSkipParent(conf: ConfigRef; cache: IdentCache; + cfgPath: AbsoluteFile): bool = + let prevMode = conf.skipParentDetectionMode + let prevSkip = optSkipParentConfigFiles in conf.globalOptions + conf.skipParentDetectionMode = true + try: + result = readConfigFile(cfgPath, cache, conf) and + optSkipParentConfigFiles in conf.globalOptions + finally: + conf.skipParentDetectionMode = prevMode + if prevSkip: + incl(conf.globalOptions, optSkipParentConfigFiles) + else: + excl(conf.globalOptions, optSkipParentConfigFiles) + proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator) = setDefaultLibpath(conf) template readConfigFile(path) = @@ -277,12 +296,24 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: runNimScriptIfExists(getUserConfigPath(DefaultConfigNims)) let pd = if not conf.projectPath.isEmpty: conf.projectPath else: AbsoluteDir(getCurrentDir()) - if optSkipParentConfigFiles notin conf.globalOptions: - for dir in parentDirs(pd.string, fromRoot=true, inclusive=false): - readConfigFile(AbsoluteDir(dir) / cfg) + if optSkipParentConfigFiles notin conf.globalOptions and not configEnablesSkipParent(conf, cache, pd / cfg): + var parentDirsSeq: seq[AbsoluteDir] = @[] + for dir in parentDirs(pd.string, inclusive=false): + let + adir = AbsoluteDir(dir) + cfgPath = adir / cfg + if not fileExists(cfgPath): + continue + parentDirsSeq.add adir + if configEnablesSkipParent(conf, cache, cfgPath): + break + + for i in countdown(parentDirsSeq.len - 1, 0): + let parentDir = parentDirsSeq[i] + readConfigFile(parentDir / cfg) if cfg == DefaultConfig: - runNimScriptIfExists(AbsoluteDir(dir) / DefaultConfigNims) + runNimScriptIfExists(parentDir / DefaultConfigNims) if optSkipProjConfigFile notin conf.globalOptions: readConfigFile(pd / cfg) diff --git a/compiler/options.nim b/compiler/options.nim index 142080cc8f5c4..48b522f0c0c68 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -453,6 +453,7 @@ type expandPosition*: TLineInfo currentConfigDir*: string # used for passPP only; absolute dir + skipParentDetectionMode*: bool # true while probing configs for skipParentCfg clientProcessId*: int From 3f75c155ab77a43e04614c1594c47cf900ba8801 Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Sat, 22 Nov 2025 16:59:11 -0500 Subject: [PATCH 2/5] forgot about nimscript --- compiler/nimconf.nim | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 4d80b3e6e5f7f..4f397c27acd12 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -297,23 +297,23 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: let pd = if not conf.projectPath.isEmpty: conf.projectPath else: AbsoluteDir(getCurrentDir()) if optSkipParentConfigFiles notin conf.globalOptions and not configEnablesSkipParent(conf, cache, pd / cfg): - var parentDirsSeq: seq[AbsoluteDir] = @[] + var parentDirs: seq[tuple[path: AbsoluteDir, hasNs: bool]] = @[] for dir in parentDirs(pd.string, inclusive=false): - let - adir = AbsoluteDir(dir) - cfgPath = adir / cfg - if not fileExists(cfgPath): + var thisReg = (path: AbsoluteDir(dir), hasNs: false) + let cfgPath = thisReg.path / cfg + if cfg == DefaultConfig: + thisReg.hasNs = fileExists(thisReg.path / DefaultConfigNims) + if not (thisReg.hasNs or fileExists(cfgPath)): continue - parentDirsSeq.add adir + parentDirs.add thisReg if configEnablesSkipParent(conf, cache, cfgPath): break - for i in countdown(parentDirsSeq.len - 1, 0): - let parentDir = parentDirsSeq[i] - readConfigFile(parentDir / cfg) - - if cfg == DefaultConfig: - runNimScriptIfExists(parentDir / DefaultConfigNims) + for i in countdown(parentDirs.len - 1, 0): + let thisReg = parentDirs[i] + readConfigFile(thisReg.path / cfg) + if thisReg.hasNs: + runNimScriptIfExists(thisReg.path / DefaultConfigNims) if optSkipProjConfigFile notin conf.globalOptions: readConfigFile(pd / cfg) From 964357b73e3463568c856353a422639034c9bff3 Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Tue, 25 Nov 2025 18:13:40 -0500 Subject: [PATCH 3/5] fix `skipParentCfg` in project cfg --- compiler/nimconf.nim | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 4f397c27acd12..52799a1469bf6 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -294,9 +294,21 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: if cfg == DefaultConfig: runNimScriptIfExists(getUserConfigPath(DefaultConfigNims)) - - let pd = if not conf.projectPath.isEmpty: conf.projectPath else: AbsoluteDir(getCurrentDir()) - if optSkipParentConfigFiles notin conf.globalOptions and not configEnablesSkipParent(conf, cache, pd / cfg): + + var + hasProjectCfg = conf.projectName.len != 0 + projectConfig = AbsoluteFile "" + if hasProjectCfg: + projectConfig = changeFileExt(conf.projectFull, "nimcfg") + if not fileExists(projectConfig): + projectConfig = changeFileExt(conf.projectFull, "nim.cfg") + if not fileExists(projectConfig): + hasProjectCfg = false + + let pd = if conf.projectPath.isEmpty: AbsoluteDir(getCurrentDir()) else: conf.projectPath + if optSkipParentConfigFiles notin conf.globalOptions and + not configEnablesSkipParent(conf, cache, pd / cfg) and + not(hasProjectCfg and configEnablesSkipParent(conf, cache, projectConfig)): var parentDirs: seq[tuple[path: AbsoluteDir, hasNs: bool]] = @[] for dir in parentDirs(pd.string, inclusive=false): var thisReg = (path: AbsoluteDir(dir), hasNs: false) @@ -320,14 +332,9 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: if cfg == DefaultConfig: runNimScriptIfExists(pd / DefaultConfigNims) - if conf.projectName.len != 0: - # new project wide config file: - var projectConfig = changeFileExt(conf.projectFull, "nimcfg") - if not fileExists(projectConfig): - projectConfig = changeFileExt(conf.projectFull, "nim.cfg") + if hasProjectCfg: readConfigFile(projectConfig) - let scriptFile = conf.projectFull.changeFileExt("nims") let scriptIsProj = scriptFile == conf.projectFull template showHintConf = From 2cb8dd6c51ae7a522a165bc4a93312036da86864 Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Tue, 25 Nov 2025 23:02:08 -0500 Subject: [PATCH 4/5] concept patch --- compiler/concepts.nim | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 7e547b19ce3c5..5c555c4169896 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -263,6 +263,19 @@ proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind = return mkNoMatch return mkSubset + +proc isObjectSubtype(a, f: PType): bool = + assert a.kind == tyObject + var t = a.baseClass + var last = a.baseClass + while t != nil and not sameObjectTypes(f, t): + if t.kind != tyObject: # avoid entering generic params etc + return false + t = t.baseClass + if t == nil: + return false + t != nil + proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool = ## The heart of the concept matching process. 'f' is the formal parameter of some ## routine inside the concept that we're looking for. 'a' is the formal parameter @@ -327,6 +340,8 @@ proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool = result = a.base.sym == f.sym else: result = sameType(f, a) + if not(result) and f.kind == tyObject: + result = isObjectSubtype(f, a) of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid: result = a.skipTypes(ignorableForArgType).kind == f.kind of tyBool, tyChar, tyInt..tyUInt64: From d1ada5500b45a45e4d4c13d5693a95a66f23b7f0 Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Sat, 29 Nov 2025 20:40:00 -0500 Subject: [PATCH 5/5] add dryrun error handler --- compiler/nimconf.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 52799a1469bf6..1501257b4b41a 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -218,11 +218,14 @@ proc parseAssignment(L: var Lexer, tok: var Token; processSwitch(s, val, passPP, info, config) proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache; - config: ConfigRef): bool = + config: ConfigRef, dryrun=false): bool = var L: Lexer = default(Lexer) tok: Token stream: PLLStream + if dryrun: + L.errorHandler = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = + discard stream = llStreamOpen(filename, fmRead) if stream != nil: openLexer(L, filename, stream, cache, config) @@ -254,7 +257,7 @@ proc configEnablesSkipParent(conf: ConfigRef; cache: IdentCache; let prevSkip = optSkipParentConfigFiles in conf.globalOptions conf.skipParentDetectionMode = true try: - result = readConfigFile(cfgPath, cache, conf) and + result = readConfigFile(cfgPath, cache, conf, dryrun=true) and optSkipParentConfigFiles in conf.globalOptions finally: conf.skipParentDetectionMode = prevMode @@ -333,6 +336,7 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: runNimScriptIfExists(pd / DefaultConfigNims) if hasProjectCfg: + # new project wide config file: readConfigFile(projectConfig) let scriptFile = conf.projectFull.changeFileExt("nims")