diff --git a/compiler/commands.nim b/compiler/commands.nim index 415fe6b352f7..94bf5105b4f1 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/concepts.nim b/compiler/concepts.nim index 7e547b19ce3c..5c555c416989 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: diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 5d31dea57f7e..1501257b4b41 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) @@ -214,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) @@ -244,6 +251,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, dryrun=true) 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) = @@ -275,28 +297,48 @@ 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: - for dir in parentDirs(pd.string, fromRoot=true, inclusive=false): - readConfigFile(AbsoluteDir(dir) / 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) + let cfgPath = thisReg.path / cfg if cfg == DefaultConfig: - runNimScriptIfExists(AbsoluteDir(dir) / DefaultConfigNims) + thisReg.hasNs = fileExists(thisReg.path / DefaultConfigNims) + if not (thisReg.hasNs or fileExists(cfgPath)): + continue + parentDirs.add thisReg + if configEnablesSkipParent(conf, cache, cfgPath): + break + + 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) if cfg == DefaultConfig: runNimScriptIfExists(pd / DefaultConfigNims) - if conf.projectName.len != 0: + if hasProjectCfg: # new project wide config file: - var projectConfig = changeFileExt(conf.projectFull, "nimcfg") - if not fileExists(projectConfig): - projectConfig = changeFileExt(conf.projectFull, "nim.cfg") readConfigFile(projectConfig) - let scriptFile = conf.projectFull.changeFileExt("nims") let scriptIsProj = scriptFile == conf.projectFull template showHintConf = diff --git a/compiler/options.nim b/compiler/options.nim index 142080cc8f5c..48b522f0c0c6 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