From afc6693c08cfb664eab657bc2c9ea47fb5516a33 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Aug 2019 02:47:24 -0700 Subject: [PATCH 01/17] resolveSymbol: fix https://github.com/nim-lang/RFCs/issues/164 D20190825T173945 --- compiler/ast.nim | 4 ++-- compiler/semcall.nim | 11 +++++++++-- compiler/semexprs.nim | 41 ++++++++++++++++++++++++++++++++++++++--- compiler/vm.nim | 2 +- lib/system.nim | 8 ++++++++ 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 155af637580ac..52b926db9a65c 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -495,6 +495,7 @@ type nfExecuteOnReload # A top-level statement that will be executed during reloads nfLastRead # this node is a last read nfFirstWrite# this node is a first write + nfOverloadResolve # return resolved `foo` in `foo(args)` TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~40) @@ -691,8 +692,7 @@ type mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2, mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples, mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, - mSymIsInstantiationOf, mNodeId - + mSymIsInstantiationOf, mNodeId, mOverloadResolve, # things that we can evaluate safely at compile time, even if not asked for it: const diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 8f29bdf328f86..0797e63e9eca2 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -520,6 +520,11 @@ proc semResolvedCall(c: PContext, x: TCandidate, markUsed(c, info, finalCallee) onUse(info, finalCallee) assert finalCallee.ast != nil + if nfOverloadResolve in n.flags: + # CHECKME: see if handling of `hasFauxMatch` is correct + let info2 = info + return newSymNode(finalCallee, info2) + if x.hasFauxMatch: result = x.call result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) @@ -568,6 +573,8 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, filter: TSymKinds, flags: TExprFlags): PNode {.nosinks.} = var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) + template canError(): bool = + efNoUndeclared notin flags and nfOverloadResolve notin n.flags if r.state == csMatch: # this may be triggered, when the explain pragma is used if errors.len > 0: @@ -595,14 +602,14 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # repeat the overload resolution, # this time enabling all the diagnostic output (this should fail again) discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) - elif efNoUndeclared notin flags: + elif canError(): notFoundError(c, n, errors) else: if efExplain notin flags: # repeat the overload resolution, # this time enabling all the diagnostic output (this should fail again) discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) - elif efNoUndeclared notin flags: + elif canError(): notFoundError(c, n, errors) proc explicitGenericInstError(c: PContext; n: PNode): PNode = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 256c008c93134..d4a8957b2ccfd 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -865,6 +865,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags) if result != nil: + if nfOverloadResolve in n.flags: return if result[0].kind != nkSym: internalError(c.config, "semOverloadedCallAnalyseEffects") return @@ -1031,6 +1032,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = let nOrig = n.copyTree #semLazyOpAux(c, n) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) + if nfOverloadResolve in n.flags: return if result != nil: result = afterCallActions(c, result, nOrig, flags) else: result = errorNode(c, n) @@ -1359,6 +1361,11 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n) var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule}) + if nfOverloadResolve in n.flags: + if s == nil: + localError(c.config, n.info, "builtinFieldAccess: qualifiedLookUp failed") + else: result = symChoice(c, n, s, scClosed) + return if s != nil: if s.kind in OverloadableSyms: result = symChoice(c, n, s, scClosed) @@ -2173,12 +2180,36 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = # we replace this node by a 'true' or 'false' node: - if n.len != 2: return semDirectOp(c, n, flags) - + if n.len != 2: return semDirectOp(c, n, flags) # why needed? result = newIntNode(nkIntLit, ord(tryExpr(c, n[1], flags) != nil)) result.info = n.info result.typ = getSysType(c.graph, n.info, tyBool) +proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags): PNode = + if sonsLen(n) != 2: + localError(c.config, n.info, "semOverloadResolve: got" & $sonsLen(n)) + return + doAssert sonsLen(n) == 2, $sonsLen(n) + let n1 = n[1] + n1.flags.incl nfOverloadResolve + case n1.kind + of {nkIdent,nkDotExpr,nkAccQuoted} + nkCallKinds - {nkHiddenCallConv}: + let flags = flags + {efWantIterator} # so that it also works for iterators + result = semExpr(c, n1, flags) + # TODO: customize what happens for result == nil + if result != nil: + doAssert result.kind in {nkSym, nkClosedSymChoice}, $result.kind + else: + localError(c.config, n.info, "expected routine, got " & $n1.kind) + if result == nil: + result = newNodeIT(nkNilLit, n.info, getSysType(c.graph, n.info, tyNil)) + elif result.kind != nkSym: + # avoids degenerating symchoice to a sym + let typ = newTypeS(tyTuple, c) + let result0 = result + result = newNodeIT(nkTupleConstr, n.info, typ) + addSon(result, result0) + proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = if n.len == 3: # XXX ugh this is really a hack: shallowCopy() can be overloaded only @@ -2250,6 +2281,9 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mCompiles: markUsed(c, n.info, s) result = semCompiles(c, setMs(n, s), flags) + of mOverloadResolve: + markUsed(c, n.info, s) + result = semOverloadResolve(c, setMs(n, s), flags) of mIs: markUsed(c, n.info, s) result = semIs(c, setMs(n, s), flags) @@ -2702,7 +2736,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} var s = qualifiedLookUp(c, n, checks) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) - if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: + if nfOverloadResolve in n.flags: result = symChoice(c, n, s, scClosed) + elif s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) if result.kind == nkSym: diff --git a/compiler/vm.nim b/compiler/vm.nim index e3ba2994f537b..088593fbc05d2 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1123,7 +1123,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var a = regs[rb].node if a.kind == nkVarTy: a = a[0] if a.kind == nkSym: - regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit) + regs[ra].node = if a.sym.ast.isNil: newNodeI(nkNilLit, a.sym.info) # preserve `info`, eg for module symbols else: copyTree(a.sym.ast) regs[ra].node.flags.incl nfIsRef else: diff --git a/lib/system.nim b/lib/system.nim index 6e84aca667b49..67624ae069626 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2793,6 +2793,14 @@ type NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj ## Represents a Nim AST node. Macros operate on this type. +proc resolveSymbol*(x: untyped): NimNode {.magic: "OverloadResolve", noSideEffect, compileTime.} = + ## resolves a symbol given an expression, eg: in `resolveSymbol(foo(args))` + ## it will find the symbol that would be called after overload resolution, + ## without calling it. Unlike `compiles(foo(args))`, the body is not analyzed. + ## Also works with `compiles(mymod.mysym)` to return the symChoice overload + ## set. + discard + when defined(nimV2): import system/repr_v2 export repr_v2 From 0dd4990350b57d782d83b2076dd3b7642fce81f7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Aug 2019 17:51:54 -0700 Subject: [PATCH 02/17] add tests from D20190825T173945 --- tests/magics/mresolve_overloads.nim | 8 ++ tests/magics/mresolves.nim | 45 ++++++++ tests/magics/tresolve_overloads.nim | 165 ++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 tests/magics/mresolve_overloads.nim create mode 100644 tests/magics/mresolves.nim create mode 100644 tests/magics/tresolve_overloads.nim diff --git a/tests/magics/mresolve_overloads.nim b/tests/magics/mresolve_overloads.nim new file mode 100644 index 0000000000000..ae0f559692444 --- /dev/null +++ b/tests/magics/mresolve_overloads.nim @@ -0,0 +1,8 @@ +let foo1* = [1,2] ## c1 +var foo2* = "asdf" ## c2 +const foo3* = 'a' ## c3 + +proc `@@@`*(a: int) = discard +proc `@@@`*(a: float) = discard +proc `@@@`*[T: Ordinal](a: T) = discard + diff --git a/tests/magics/mresolves.nim b/tests/magics/mresolves.nim new file mode 100644 index 0000000000000..eb1837f607402 --- /dev/null +++ b/tests/magics/mresolves.nim @@ -0,0 +1,45 @@ +import std/macros + +macro overloadExistsImpl(x: typed): bool = + newLit(x != nil) + +template overloadExists*(a: untyped): bool = + overloadExistsImpl(resolveSymbol(a)) + +type InstantiationInfo = type(instantiationInfo()) + +proc toStr(info: InstantiationInfo | LineInfo): string = + const offset = 1 + result = info.filename & ":" & $info.line & ":" & $(info.column + offset) + +proc inspectImpl*(s: var string, a: NimNode, resolveLet: bool) = + var a = a + if resolveLet: + a = a.getImpl + a = a[2] + case a.kind + of nnkClosedSymChoice: + s.add "closedSymChoice:" + for ai in a: + s.add "\n " + inspectImpl(s, ai, false) + of nnkSym: + var a2 = a.getImpl + const callables = {nnkProcDef, nnkMethodDef, nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef} + if a2.kind in callables: + let a20=a2 + a2 = newTree(a20.kind) + for i, ai in a20: + a2.add if i notin [6]: ai else: newEmptyNode() + s.add a2.lineInfoObj.toStr & " " & a2.repr + else: error($a.kind, a) + +macro inspect*(a: typed, resolveLet: static bool = false): untyped = + var a = a + if a.kind == nnkTupleConstr: + a = a[0] + var s: string + s.add a.lineInfoObj.toStr & ": " + s.add a.repr & " = " + inspectImpl(s, a, resolveLet) + echo s diff --git a/tests/magics/tresolve_overloads.nim b/tests/magics/tresolve_overloads.nim new file mode 100644 index 0000000000000..a8d53ead2dd25 --- /dev/null +++ b/tests/magics/tresolve_overloads.nim @@ -0,0 +1,165 @@ +#[ +D20190825T173945 +]# + +import ./mresolves + +template bail() = static: doAssert false + +proc funDecl1(a: int) {.importc.} +proc funDecl2(a: int) + +proc fun(a: int) = discard + +template fun2(a = 0) = bail() +template fun3() = bail() + +proc fun4(a: float) = discard + +proc fun4[T](a: T) = + static: echo "in fun4" + bail() + +macro fun5(a: typed): untyped = discard +macro fun6(a: untyped): untyped = discard +iterator fun7(a: int): auto = yield 1 + +proc fun8(a: int): auto = (a, "int") +proc fun8(a: float): tuple = (a,"float") +template fun8(a: string) = discard +template fun8(a: char = 'x') = discard +template fun8() = discard +macro fun8(b = 1'u8): untyped = discard +macro fun8(c: static bool): untyped = discard + +proc fun8(d: var int) = d.inc + +proc main()= + static: + doAssert overloadExists(fun4(1)) + doAssert not compiles(fun4(1)) + doAssert overloadExists(fun4(1)) + doAssert overloadExists(fun4(1.2)) + doAssert not overloadExists(fun4()) + # doAssert not overloadExists(nonexistant(1)) # should we error with `Error: undeclared identifier: 'nonexistant'` ? A: probly should just return false, eg: imagine for: ` 1 @ 2` + + doAssert overloadExists(funDecl1(1)) + doAssert not overloadExists(funDecl1(1.0)) + doAssert overloadExists(funDecl2(1)) + + doAssert overloadExists(fun(1)) + doAssert overloadExists(1+1) + doAssert not overloadExists('a'+1.2) + + doAssert not overloadExists(fun(1.1)) + doAssert not overloadExists(fun(1, 2)) + doAssert overloadExists(fun2()) + doAssert overloadExists(fun2(1)) + doAssert overloadExists(fun3()) + doAssert not overloadExists(fun3(1)) + + # subtlety: if arguments for a `typed` formal param are not well typed, + # we error instead of return false + doAssert not compiles overloadExists(fun5(1 + 'a')) + + doAssert overloadExists(fun5(1 + 1)) + doAssert not overloadExists(fun5(1 + 1, 2)) + doAssert overloadExists(fun6(1 + 'a')) + doAssert not overloadExists(fun6(1 + 'a', 2)) + doAssert overloadExists(fun7(1)) + doAssert not overloadExists(fun7()) + + doAssert resolveSymbol(fun8(1))(3) == fun8(3) + inspect resolveSymbol(fun8) + + inspect resolveSymbol(fun7) + inspect resolveSymbol(fun8(1)) + inspect resolveSymbol(fun8(1.2)) + inspect resolveSymbol(fun8("asdf")) + inspect resolveSymbol(fun8('a')) + doAssert compiles(resolveSymbol(fun8('a'))) + doAssert not compiles(resolveSymbol(fun8())) # correctly would give ambiguous error + inspect resolveSymbol(fun8(b = 1)) + inspect resolveSymbol(fun8(c = false)) + inspect resolveSymbol(1.1 / 2.0) + + block: + var c1 = false + doAssert not overloadExists(fun8(c = c1)) + const c2 = false + doAssert overloadExists(fun8(c = c2)) + var c3 = 10 + doAssert overloadExists(fun8(d = c3)) + doAssert c3 == 10 + resolveSymbol(fun8(d = c3))(d = c3) + doAssert c3 == 11 + let t = resolveSymbol(fun8(d = c3)) + doAssert type(t) is proc + t(d = c3) + doAssert c3 == 12 + + block: + var z = 10 + proc fun9(z0: int) = z+=z0 + proc fun9(z0: float) = doAssert false + let t = resolveSymbol(fun9(12)) + # can't work with `const t = fun9`: invalid type for const + doAssert type(t) is proc + t(3) + doAssert z == 10+3 + inspect resolveSymbol(fun9(12)) + inspect(resolveSymbol(t), resolveLet=true) + +import std/strutils +import std/macros +import std/macros as macrosAlias +import ./mresolve_overloads + +proc main2()= + block: + inspect resolveSymbol(`@@@`) + + let t = resolveSymbol toUpper("asdf") + inspect resolveSymbol(t), resolveLet=true + doAssert t("asdf") == "ASDF" + let t2 = resolveSymbol strutils.toUpper("asdf") + inspect resolveSymbol(t2), resolveLet=true + doAssert t2("asdf") == "ASDF" + + inspect resolveSymbol(strutils.toUpper) + inspect resolveSymbol(strutils.`toUpper`) + inspect resolveSymbol(`toUpper`) + # overloaded + inspect resolveSymbol(`$`) + inspect resolveSymbol(system.`$`) + + doAssert compiles resolveSymbol(system.compiles) + inspect resolveSymbol(system.compiles) + doAssert not compiles resolveSymbol(system.nonexistant) + doAssert not compiles resolveSymbol(nonexistant) + + block: + template bar1(): untyped = 12 + inspect resolveSymbol(bar1) + inspect resolveSymbol(currentSourcePath) + inspect resolveSymbol(system.currentSourcePath) + doAssert resolveSymbol(system.currentSourcePath)() == currentSourcePath() + inspect resolveSymbol(system.uint16) + inspect resolveSymbol(system.cint) + inspect resolveSymbol(cint) + inspect resolveSymbol(system.off) + inspect resolveSymbol(newLit(true)) + inspect resolveSymbol(foo1) + inspect resolveSymbol(foo2) + inspect resolveSymbol(foo3) + inspect resolveSymbol(mresolve_overloads.foo3) + inspect resolveSymbol(macros.nnkCallKinds) + + ## module + inspect resolveSymbol(macros) + inspect resolveSymbol(macrosAlias) + +proc funDecl2(a: int) = discard + +main() +main2() From 82c281d18f58f1acbac96a3b4673293bdc877afe Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 9 Mar 2020 18:57:44 -0700 Subject: [PATCH 03/17] fix after rebase --- compiler/condsyms.nim | 1 + compiler/semexprs.nim | 7 +++---- lib/system.nim | 15 ++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index bdecd7e532eca..e8fcbf916768e 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -133,3 +133,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasCustomLiterals") defineSymbol("nimHasUnifiedTuple") defineSymbol("nimHasIterable") + defineSymbol("himHasOverloadResolve") diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d4a8957b2ccfd..1e143f75fcb14 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2186,10 +2186,9 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = result.typ = getSysType(c.graph, n.info, tyBool) proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags): PNode = - if sonsLen(n) != 2: - localError(c.config, n.info, "semOverloadResolve: got" & $sonsLen(n)) + if n.len != 2: + localError(c.config, n.info, "semOverloadResolve: got" & $n.len) return - doAssert sonsLen(n) == 2, $sonsLen(n) let n1 = n[1] n1.flags.incl nfOverloadResolve case n1.kind @@ -2208,7 +2207,7 @@ proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags): PNode = let typ = newTypeS(tyTuple, c) let result0 = result result = newNodeIT(nkTupleConstr, n.info, typ) - addSon(result, result0) + result.add result0 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = if n.len == 3: diff --git a/lib/system.nim b/lib/system.nim index 67624ae069626..ee648e3fcafb3 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2793,13 +2793,14 @@ type NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj ## Represents a Nim AST node. Macros operate on this type. -proc resolveSymbol*(x: untyped): NimNode {.magic: "OverloadResolve", noSideEffect, compileTime.} = - ## resolves a symbol given an expression, eg: in `resolveSymbol(foo(args))` - ## it will find the symbol that would be called after overload resolution, - ## without calling it. Unlike `compiles(foo(args))`, the body is not analyzed. - ## Also works with `compiles(mymod.mysym)` to return the symChoice overload - ## set. - discard +when defined(himHasOverloadResolve): + proc resolveSymbol*(x: untyped): NimNode {.magic: "OverloadResolve", noSideEffect, compileTime.} = + ## resolves a symbol given an expression, eg: in `resolveSymbol(foo(args))` + ## it will find the symbol that would be called after overload resolution, + ## without calling it. Unlike `compiles(foo(args))`, the body is not analyzed. + ## Also works with `compiles(mymod.mysym)` to return the symChoice overload + ## set. + discard when defined(nimV2): import system/repr_v2 From 65eebc9f1319302f578c06484ec9acad3288cc02 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 9 Mar 2020 19:05:00 -0700 Subject: [PATCH 04/17] fixup --- lib/system.nim | 1 - tests/magics/mresolves.nim | 3 ++- tests/magics/tresolve_overloads.nim | 10 +++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index ee648e3fcafb3..7ab942ee8f39f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2800,7 +2800,6 @@ when defined(himHasOverloadResolve): ## without calling it. Unlike `compiles(foo(args))`, the body is not analyzed. ## Also works with `compiles(mymod.mysym)` to return the symChoice overload ## set. - discard when defined(nimV2): import system/repr_v2 diff --git a/tests/magics/mresolves.nim b/tests/magics/mresolves.nim index eb1837f607402..47910642dc1c8 100644 --- a/tests/magics/mresolves.nim +++ b/tests/magics/mresolves.nim @@ -42,4 +42,5 @@ macro inspect*(a: typed, resolveLet: static bool = false): untyped = s.add a.lineInfoObj.toStr & ": " s.add a.repr & " = " inspectImpl(s, a, resolveLet) - echo s + when defined(nimTestsResolvesDebug): + echo s diff --git a/tests/magics/tresolve_overloads.nim b/tests/magics/tresolve_overloads.nim index a8d53ead2dd25..fee089e5175aa 100644 --- a/tests/magics/tresolve_overloads.nim +++ b/tests/magics/tresolve_overloads.nim @@ -1,5 +1,13 @@ #[ -D20190825T173945 +use -d:nimTestsResolvesDebug to make the `inspect` macro print debug info showing +resolved symbol locations, for example: + +`inspect resolveSymbol(`$`)` would print: + +Nim/tests/magics/tresolve_overloads.nim:133:28: $ = closedSymChoice: + Nim/lib/system/dollars.nim:124:1 proc `$`[T](x: set[T]): string + Nim/lib/system/dollars.nim:140:1 proc `$`[T; U](x: HSlice[T, U]): string + Nim/lib/system/dollars.nim:14:1 proc `$`(x: bool): string {.magic: "BoolToStr", noSideEffect.} ]# import ./mresolves From b3804336fae7fbf2ffc8c48e04646453a67eec7f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 21 Mar 2020 03:37:48 -0700 Subject: [PATCH 05/17] _ --- tests/magics/tresolve_overloads.nim | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/magics/tresolve_overloads.nim b/tests/magics/tresolve_overloads.nim index fee089e5175aa..ce9cca09e700a 100644 --- a/tests/magics/tresolve_overloads.nim +++ b/tests/magics/tresolve_overloads.nim @@ -77,6 +77,19 @@ proc main()= doAssert overloadExists(fun7(1)) doAssert not overloadExists(fun7()) + block: # dot accesors + type Foo = object + bar1: int + template bar2(a: Foo) = discard + template bar3[T](a: T) = discard + doAssert compiles(Foo().bar1) + doAssert overloadExists(Foo().bar1) + var foo: Foo + doAssert overloadExists(foo.bar1) + doAssert overloadExists(foo.bar2) + doAssert overloadExists(foo.bar3) + doAssert not overloadExists(foo.bar4) + doAssert resolveSymbol(fun8(1))(3) == fun8(3) inspect resolveSymbol(fun8) From d8fcc6545b7210713be5ce37806146af222a2f39 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 21 Mar 2020 17:49:43 -0700 Subject: [PATCH 06/17] _ --- compiler/semexprs.nim | 56 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1e143f75fcb14..d7cc099eff7ec 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -13,6 +13,8 @@ when defined(nimCompilerStackraceHints): import std/stackframes +import std/wrapnils # PRTEMP + const errExprXHasNoType = "expression '$1' has no type (or is ambiguous)" errXExpectsTypeOrValue = "'$1' expects a type or value" @@ -1031,8 +1033,12 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # this seems to be a hotspot in the compiler! let nOrig = n.copyTree #semLazyOpAux(c, n) + echo0b (n.kind, n.flags) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) - if nfOverloadResolve in n.flags: return + echo0b ?.result.kind + if nfOverloadResolve in n.flags: + echo0b result == nil + return if result != nil: result = afterCallActions(c, result, nOrig, flags) else: result = errorNode(c, n) @@ -1361,7 +1367,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n) var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule}) - if nfOverloadResolve in n.flags: + # if nfOverloadResolve in n.flags: + if nfOverloadResolve in n.flags and false: if s == nil: localError(c.config, n.info, "builtinFieldAccess: qualifiedLookUp failed") else: result = symChoice(c, n, s, scClosed) @@ -1448,6 +1455,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = tryReadingGenericParam(c, n, i, t) proc dotTransformation(c: PContext, n: PNode): PNode = + echo0b (isSymChoice(n[1]), n[1].kind) if isSymChoice(n[1]): result = newNodeI(nkDotCall, n.info) result.add n[1] @@ -1463,6 +1471,9 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # this is difficult, because the '.' is used in many different contexts # in Nim. We first allow types in the semantic checking. result = builtinFieldAccess(c, n, flags) + echo0b ("semFieldAccess", result != nil, c.config$n.info) + if result != nil: + echo0b result.kind if result == nil: result = dotTransformation(c, n) @@ -2185,24 +2196,32 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = result.info = n.info result.typ = getSysType(c.graph, n.info, tyBool) -proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags): PNode = - if n.len != 2: - localError(c.config, n.info, "semOverloadResolve: got" & $n.len) - return - let n1 = n[1] +proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags, n1: PNode = nil): PNode = + let isTopLevel = n1 == nil + var n1 = n1 + if n1 == nil: + if n.len != 2: + localError(c.config, n.info, "semOverloadResolve: got" & $n.len) + return + n1 = n[1] + echo0b (n1.kind, n1.flags) n1.flags.incl nfOverloadResolve case n1.kind of {nkIdent,nkDotExpr,nkAccQuoted} + nkCallKinds - {nkHiddenCallConv}: let flags = flags + {efWantIterator} # so that it also works for iterators result = semExpr(c, n1, flags) + echo0b result == nil # TODO: customize what happens for result == nil if result != nil: + echo0b result.kind doAssert result.kind in {nkSym, nkClosedSymChoice}, $result.kind else: localError(c.config, n.info, "expected routine, got " & $n1.kind) if result == nil: - result = newNodeIT(nkNilLit, n.info, getSysType(c.graph, n.info, tyNil)) + if isTopLevel: + result = newNodeIT(nkNilLit, n.info, getSysType(c.graph, n.info, tyNil)) elif result.kind != nkSym: + echo0b () # avoids degenerating symchoice to a sym let typ = newTypeS(tyTuple, c) let result0 = result @@ -2723,6 +2742,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = when defined(nimCompilerStackraceHints): setFrameMsg c.config$n.info & " " & $n.kind result = n + echo0b (c.config$n.info, n.kind, flags) + # defer: + # echo0b: (c.config$n.info, "end", n.kind, flags, result.kind) if c.config.cmd == cmdIdeTools: suggestExpr(c, n) if nfSem in n.flags: return case n.kind @@ -2789,10 +2811,20 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkCharLit: if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyChar) of nkDotExpr: + let hasOverloadResolve = nfOverloadResolve in n.flags + echo0b (n.flags) result = semFieldAccess(c, n, flags) + echo0b (result.kind, result.flags) if result.kind == nkDotCall: result.transitionSonsKind(nkCall) - result = semExpr(c, result, flags) + echo0b (result.kind, result.flags, result[0].kind, result[0].flags, result.len) + if result[0].kind == nkIdent: + echo0b (result[0].ident.s, hasOverloadResolve) + if hasOverloadResolve: + result = semOverloadResolve(c, result, flags, result) + else: + result = semExpr(c, result, flags) + echo0b (?.result.kind) of nkBind: message(c.config, n.info, warnDeprecated, "bind is deprecated") result = semExpr(c, n[0], flags) @@ -2813,9 +2845,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = checkMinSonsLen(n, 1, c.config) #when defined(nimsuggest): # if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n) - let mode = if nfDotField in n.flags: {} else: {checkUndeclared} + echo0b (n.flags) + let mode = if {nfDotField, nfOverloadResolve} * n.flags != {}: {} else: {checkUndeclared} c.isAmbiguous = false var s = qualifiedLookUp(c, n[0], mode) + echo0b (?.s.name.s, c.config$(?.s.ast.info)) if s != nil: #if c.config.cmd == cmdNimfix and n[0].kind == nkDotExpr: # pretty.checkUse(n[0][1].info, s) @@ -2834,6 +2868,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = elif s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) of skProc, skFunc, skMethod, skConverter, skIterator: + echo0b (s.kind, s.magic) if s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) else: @@ -3010,3 +3045,4 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = localError(c.config, n.info, "invalid expression: " & renderTree(n, {renderNoComments})) if result != nil: incl(result.flags, nfSem) + echo0b result == nil From 2483847da9a9e41721ba0fe6d251fdac4c3d4b15 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 21 Mar 2020 20:18:15 -0700 Subject: [PATCH 07/17] _ --- compiler/semcall.nim | 1 + compiler/semexprs.nim | 32 ++++++++++++++++++++++------- tests/magics/tresolve_overloads.nim | 10 ++++----- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 0797e63e9eca2..8b8f5231bb0d2 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -572,6 +572,7 @@ proc tryDeref(n: PNode): PNode = proc semOverloadedCall(c: PContext, n, nOrig: PNode, filter: TSymKinds, flags: TExprFlags): PNode {.nosinks.} = var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil + echo0b (flags, n.flags, nOrig.flags, n.kind) var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) template canError(): bool = efNoUndeclared notin flags and nfOverloadResolve notin n.flags diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d7cc099eff7ec..b4d044faa0260 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -68,6 +68,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode = rejectEmptyNode(n) result = semExpr(c, n, flags+{efWantValue}) + if result == nil: return errorNode(c, n) let isEmpty = result.kind == nkEmpty @@ -950,7 +951,12 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: n[0] = n0 else: - n[0] = semExpr(c, n[0], {efInCall}) + echo0b (flags, n.flags, n.renderTree) + var flags2 = {efInCall} + if nfOverloadResolve in n.flags: + flags2.incl {efNoUndeclared} + n[0] = semExpr(c, n[0], flags2) + if n[0] == nil and nfOverloadResolve in n.flags: return nil let t = n[0].typ if t != nil and t.kind in {tyVar, tyLent}: n[0] = newDeref(n[0]) @@ -1366,11 +1372,16 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = suggestExpr(c, n) if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n) - var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule}) + echo0b (n.flags, n.kind, n.renderTree, flags) + var flags2 = {checkAmbiguity, checkUndeclared, checkModule} + # if nfOverloadResolve in n.flags: flags2.excl checkUndeclared + var s = qualifiedLookUp(c, n, flags2) + echo0b (n.flags, s == nil) # if nfOverloadResolve in n.flags: - if nfOverloadResolve in n.flags and false: + if false and nfOverloadResolve in n.flags: # could be nil for somobj.somefield if s == nil: - localError(c.config, n.info, "builtinFieldAccess: qualifiedLookUp failed") + return nil + # localError(c.config, n.info, "builtinFieldAccess: qualifiedLookUp failed") else: result = symChoice(c, n, s, scClosed) return if s != nil: @@ -2208,7 +2219,9 @@ proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags, n1: PNode = ni n1.flags.incl nfOverloadResolve case n1.kind of {nkIdent,nkDotExpr,nkAccQuoted} + nkCallKinds - {nkHiddenCallConv}: - let flags = flags + {efWantIterator} # so that it also works for iterators + # let flags = flags + {efWantIterator} + # so that it also works for iterators and allows nonexistant fields and procs + let flags = flags + {efWantIterator, efNoUndeclared} result = semExpr(c, n1, flags) echo0b result == nil # TODO: customize what happens for result == nil @@ -2742,20 +2755,22 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = when defined(nimCompilerStackraceHints): setFrameMsg c.config$n.info & " " & $n.kind result = n - echo0b (c.config$n.info, n.kind, flags) + echo0b (c.config$n.info, n.kind, flags, n.flags) # defer: # echo0b: (c.config$n.info, "end", n.kind, flags, result.kind) if c.config.cmd == cmdIdeTools: suggestExpr(c, n) if nfSem in n.flags: return case n.kind of nkIdent, nkAccQuoted: - let checks = if efNoEvaluateGeneric in flags: + var checks = if efNoEvaluateGeneric in flags: {checkUndeclared, checkPureEnumFields} elif efInCall in flags: {checkUndeclared, checkModule, checkPureEnumFields} else: {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} + if efNoUndeclared in flags: checks.excl checkUndeclared var s = qualifiedLookUp(c, n, checks) + if efNoUndeclared in flags and s == nil: return nil if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) if nfOverloadResolve in n.flags: result = symChoice(c, n, s, scClosed) elif s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: @@ -2825,6 +2840,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = else: result = semExpr(c, result, flags) echo0b (?.result.kind) + elif result.kind == nkDotExpr and hasOverloadResolve: + result = result[1] + echo0b (result.kind, result.renderTree) of nkBind: message(c.config, n.info, warnDeprecated, "bind is deprecated") result = semExpr(c, n[0], flags) diff --git a/tests/magics/tresolve_overloads.nim b/tests/magics/tresolve_overloads.nim index ce9cca09e700a..dbcc61d1c779e 100644 --- a/tests/magics/tresolve_overloads.nim +++ b/tests/magics/tresolve_overloads.nim @@ -84,7 +84,7 @@ proc main()= template bar3[T](a: T) = discard doAssert compiles(Foo().bar1) doAssert overloadExists(Foo().bar1) - var foo: Foo + const foo = Foo() doAssert overloadExists(foo.bar1) doAssert overloadExists(foo.bar2) doAssert overloadExists(foo.bar3) @@ -157,14 +157,14 @@ proc main2()= doAssert compiles resolveSymbol(system.compiles) inspect resolveSymbol(system.compiles) doAssert not compiles resolveSymbol(system.nonexistant) - doAssert not compiles resolveSymbol(nonexistant) + # doAssert not compiles resolveSymbol(nonexistant) # PRTEMP block: template bar1(): untyped = 12 inspect resolveSymbol(bar1) inspect resolveSymbol(currentSourcePath) inspect resolveSymbol(system.currentSourcePath) - doAssert resolveSymbol(system.currentSourcePath)() == currentSourcePath() + # doAssert resolveSymbol(system.currentSourcePath)() == currentSourcePath() # PRTEMP inspect resolveSymbol(system.uint16) inspect resolveSymbol(system.cint) inspect resolveSymbol(cint) @@ -173,8 +173,8 @@ proc main2()= inspect resolveSymbol(foo1) inspect resolveSymbol(foo2) inspect resolveSymbol(foo3) - inspect resolveSymbol(mresolve_overloads.foo3) - inspect resolveSymbol(macros.nnkCallKinds) + # inspect resolveSymbol(mresolve_overloads.foo3) # PRTEMP + # inspect resolveSymbol(macros.nnkCallKinds) # PRTEMP ## module inspect resolveSymbol(macros) From cabc49a33529e95b948d93be42c231897b18fc9a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 21 Mar 2020 20:37:27 -0700 Subject: [PATCH 08/17] all works w fields! --- compiler/lookups.nim | 2 +- compiler/semexprs.nim | 16 ++++++++++++++++ tests/magics/tresolve_overloads.nim | 10 +++++----- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 15a22c77875bf..779ef80868d61 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -541,7 +541,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config) else: result = someSym(c.graph, m, ident).skipAlias(n, c.config) - if result == nil and checkUndeclared in flags: + if result == nil and checkUndeclared in flags and nfOverloadResolve notin n.flags: result = errorUndeclaredIdentifierHint(c, n[1], ident) elif n[1].kind == nkSym: result = n[1].sym diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b4d044faa0260..cd8d2d33fe95f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1373,9 +1373,25 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n) echo0b (n.flags, n.kind, n.renderTree, flags) + var flags2 = {checkAmbiguity, checkUndeclared, checkModule} # if nfOverloadResolve in n.flags: flags2.excl checkUndeclared var s = qualifiedLookUp(c, n, flags2) + + if nfOverloadResolve in n.flags and n.kind == nkDotExpr: + var m = qualifiedLookUp(c, n[0], (flags2*{checkUndeclared})+{checkModule}) + if m != nil and m.kind == skModule: + # it's mymodule.someident + if s == nil: + return nil + else: + # PRTEMP + return symChoice(c, n, s, scClosed) + # return s + + # if s == nil and nfOverloadResolve in n.flags: # could be nil for somobj.somefield + + echo0b (n.flags, s == nil) # if nfOverloadResolve in n.flags: if false and nfOverloadResolve in n.flags: # could be nil for somobj.somefield diff --git a/tests/magics/tresolve_overloads.nim b/tests/magics/tresolve_overloads.nim index dbcc61d1c779e..a419a5799f4df 100644 --- a/tests/magics/tresolve_overloads.nim +++ b/tests/magics/tresolve_overloads.nim @@ -156,15 +156,15 @@ proc main2()= doAssert compiles resolveSymbol(system.compiles) inspect resolveSymbol(system.compiles) - doAssert not compiles resolveSymbol(system.nonexistant) - # doAssert not compiles resolveSymbol(nonexistant) # PRTEMP + doAssert resolveSymbol(system.nonexistant) == nil + doAssert resolveSymbol(nonexistant) == nil block: template bar1(): untyped = 12 inspect resolveSymbol(bar1) inspect resolveSymbol(currentSourcePath) inspect resolveSymbol(system.currentSourcePath) - # doAssert resolveSymbol(system.currentSourcePath)() == currentSourcePath() # PRTEMP + doAssert resolveSymbol(system.currentSourcePath)() == currentSourcePath() inspect resolveSymbol(system.uint16) inspect resolveSymbol(system.cint) inspect resolveSymbol(cint) @@ -173,8 +173,8 @@ proc main2()= inspect resolveSymbol(foo1) inspect resolveSymbol(foo2) inspect resolveSymbol(foo3) - # inspect resolveSymbol(mresolve_overloads.foo3) # PRTEMP - # inspect resolveSymbol(macros.nnkCallKinds) # PRTEMP + inspect resolveSymbol(mresolve_overloads.foo3) + inspect resolveSymbol(macros.nnkCallKinds) ## module inspect resolveSymbol(macros) From 4dbde0d271592974dc9803e96289e7dd6afded9f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 21 Mar 2020 20:43:16 -0700 Subject: [PATCH 09/17] cleanups --- compiler/semcall.nim | 1 - compiler/semexprs.nim | 47 ++----------------------------------------- 2 files changed, 2 insertions(+), 46 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 8b8f5231bb0d2..0797e63e9eca2 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -572,7 +572,6 @@ proc tryDeref(n: PNode): PNode = proc semOverloadedCall(c: PContext, n, nOrig: PNode, filter: TSymKinds, flags: TExprFlags): PNode {.nosinks.} = var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil - echo0b (flags, n.flags, nOrig.flags, n.kind) var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) template canError(): bool = efNoUndeclared notin flags and nfOverloadResolve notin n.flags diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cd8d2d33fe95f..fe648d5618f0b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -951,7 +951,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: n[0] = n0 else: - echo0b (flags, n.flags, n.renderTree) var flags2 = {efInCall} if nfOverloadResolve in n.flags: flags2.incl {efNoUndeclared} @@ -1039,12 +1038,8 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # this seems to be a hotspot in the compiler! let nOrig = n.copyTree #semLazyOpAux(c, n) - echo0b (n.kind, n.flags) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) - echo0b ?.result.kind - if nfOverloadResolve in n.flags: - echo0b result == nil - return + if nfOverloadResolve in n.flags: return if result != nil: result = afterCallActions(c, result, nOrig, flags) else: result = errorNode(c, n) @@ -1372,10 +1367,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = suggestExpr(c, n) if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n) - echo0b (n.flags, n.kind, n.renderTree, flags) - var flags2 = {checkAmbiguity, checkUndeclared, checkModule} - # if nfOverloadResolve in n.flags: flags2.excl checkUndeclared var s = qualifiedLookUp(c, n, flags2) if nfOverloadResolve in n.flags and n.kind == nkDotExpr: @@ -1385,21 +1377,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if s == nil: return nil else: - # PRTEMP return symChoice(c, n, s, scClosed) - # return s - - # if s == nil and nfOverloadResolve in n.flags: # could be nil for somobj.somefield - - echo0b (n.flags, s == nil) - # if nfOverloadResolve in n.flags: - if false and nfOverloadResolve in n.flags: # could be nil for somobj.somefield - if s == nil: - return nil - # localError(c.config, n.info, "builtinFieldAccess: qualifiedLookUp failed") - else: result = symChoice(c, n, s, scClosed) - return if s != nil: if s.kind in OverloadableSyms: result = symChoice(c, n, s, scClosed) @@ -1482,7 +1461,6 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = tryReadingGenericParam(c, n, i, t) proc dotTransformation(c: PContext, n: PNode): PNode = - echo0b (isSymChoice(n[1]), n[1].kind) if isSymChoice(n[1]): result = newNodeI(nkDotCall, n.info) result.add n[1] @@ -1498,9 +1476,6 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # this is difficult, because the '.' is used in many different contexts # in Nim. We first allow types in the semantic checking. result = builtinFieldAccess(c, n, flags) - echo0b ("semFieldAccess", result != nil, c.config$n.info) - if result != nil: - echo0b result.kind if result == nil: result = dotTransformation(c, n) @@ -2231,18 +2206,15 @@ proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags, n1: PNode = ni localError(c.config, n.info, "semOverloadResolve: got" & $n.len) return n1 = n[1] - echo0b (n1.kind, n1.flags) n1.flags.incl nfOverloadResolve case n1.kind of {nkIdent,nkDotExpr,nkAccQuoted} + nkCallKinds - {nkHiddenCallConv}: + # CHECKME # let flags = flags + {efWantIterator} # so that it also works for iterators and allows nonexistant fields and procs let flags = flags + {efWantIterator, efNoUndeclared} result = semExpr(c, n1, flags) - echo0b result == nil - # TODO: customize what happens for result == nil if result != nil: - echo0b result.kind doAssert result.kind in {nkSym, nkClosedSymChoice}, $result.kind else: localError(c.config, n.info, "expected routine, got " & $n1.kind) @@ -2250,7 +2222,6 @@ proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags, n1: PNode = ni if isTopLevel: result = newNodeIT(nkNilLit, n.info, getSysType(c.graph, n.info, tyNil)) elif result.kind != nkSym: - echo0b () # avoids degenerating symchoice to a sym let typ = newTypeS(tyTuple, c) let result0 = result @@ -2771,9 +2742,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = when defined(nimCompilerStackraceHints): setFrameMsg c.config$n.info & " " & $n.kind result = n - echo0b (c.config$n.info, n.kind, flags, n.flags) - # defer: - # echo0b: (c.config$n.info, "end", n.kind, flags, result.kind) if c.config.cmd == cmdIdeTools: suggestExpr(c, n) if nfSem in n.flags: return case n.kind @@ -2843,22 +2811,15 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyChar) of nkDotExpr: let hasOverloadResolve = nfOverloadResolve in n.flags - echo0b (n.flags) result = semFieldAccess(c, n, flags) - echo0b (result.kind, result.flags) if result.kind == nkDotCall: result.transitionSonsKind(nkCall) - echo0b (result.kind, result.flags, result[0].kind, result[0].flags, result.len) - if result[0].kind == nkIdent: - echo0b (result[0].ident.s, hasOverloadResolve) if hasOverloadResolve: result = semOverloadResolve(c, result, flags, result) else: result = semExpr(c, result, flags) - echo0b (?.result.kind) elif result.kind == nkDotExpr and hasOverloadResolve: result = result[1] - echo0b (result.kind, result.renderTree) of nkBind: message(c.config, n.info, warnDeprecated, "bind is deprecated") result = semExpr(c, n[0], flags) @@ -2879,11 +2840,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = checkMinSonsLen(n, 1, c.config) #when defined(nimsuggest): # if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n) - echo0b (n.flags) let mode = if {nfDotField, nfOverloadResolve} * n.flags != {}: {} else: {checkUndeclared} c.isAmbiguous = false var s = qualifiedLookUp(c, n[0], mode) - echo0b (?.s.name.s, c.config$(?.s.ast.info)) if s != nil: #if c.config.cmd == cmdNimfix and n[0].kind == nkDotExpr: # pretty.checkUse(n[0][1].info, s) @@ -2902,7 +2861,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = elif s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) of skProc, skFunc, skMethod, skConverter, skIterator: - echo0b (s.kind, s.magic) if s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) else: @@ -3079,4 +3037,3 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = localError(c.config, n.info, "invalid expression: " & renderTree(n, {renderNoComments})) if result != nil: incl(result.flags, nfSem) - echo0b result == nil From 2aeaf353ed5d7fb7ca0f96941f3f892c54708ce1 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 21 Mar 2020 20:52:29 -0700 Subject: [PATCH 10/17] improve tests --- compiler/semexprs.nim | 2 - tests/magics/tresolve_overloads.nim | 58 ++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fe648d5618f0b..12499cbd994ac 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -13,8 +13,6 @@ when defined(nimCompilerStackraceHints): import std/stackframes -import std/wrapnils # PRTEMP - const errExprXHasNoType = "expression '$1' has no type (or is ambiguous)" errXExpectsTypeOrValue = "'$1' expects a type or value" diff --git a/tests/magics/tresolve_overloads.nim b/tests/magics/tresolve_overloads.nim index a419a5799f4df..9fcc5551ab5b3 100644 --- a/tests/magics/tresolve_overloads.nim +++ b/tests/magics/tresolve_overloads.nim @@ -11,6 +11,7 @@ Nim/tests/magics/tresolve_overloads.nim:133:28: $ = closedSymChoice: ]# import ./mresolves +import ./mresolve_overloads template bail() = static: doAssert false @@ -43,7 +44,7 @@ macro fun8(c: static bool): untyped = discard proc fun8(d: var int) = d.inc proc main()= - static: + block: # overloadExists with nkCall doAssert overloadExists(fun4(1)) doAssert not compiles(fun4(1)) doAssert overloadExists(fun4(1)) @@ -77,19 +78,48 @@ proc main()= doAssert overloadExists(fun7(1)) doAssert not overloadExists(fun7()) - block: # dot accesors - type Foo = object - bar1: int - template bar2(a: Foo) = discard - template bar3[T](a: T) = discard - doAssert compiles(Foo().bar1) - doAssert overloadExists(Foo().bar1) - const foo = Foo() - doAssert overloadExists(foo.bar1) - doAssert overloadExists(foo.bar2) - doAssert overloadExists(foo.bar3) - doAssert not overloadExists(foo.bar4) + block: # overloadExists with nkCall dot accesors + type Foo = object + bar1: int + var foo: Foo + + template bar2(a: Foo) = discard + template bar3[T](a: T): int = 0 + + proc bar4(a: int) = discard + + template bar5(a: int) = discard + template bar5(a: Foo) = discard + + doAssert not declared(mresolve_overloads.nonexistant) + doAssert declared(mresolve_overloads.foo3) + doAssert not declared(foo.bar1) + doAssert not overloadExists(mresolve_overloads.nonexistant) + doAssert overloadExists(mresolve_overloads.foo3) + + doAssert not overloadExists(nonexistant(foo)) + doAssert not overloadExists(foo.nonexistant) + doAssert compiles(Foo().bar1) + doAssert compiles(Foo().bar1) + doAssert overloadExists(foo.bar2) + doAssert overloadExists(foo.bar3) + doAssert not overloadExists(bar4(foo)) + doAssert not overloadExists(foo.bar4) + + proc bar4(a: Foo) = discard + + doAssert overloadExists(foo.bar4) + doAssert overloadExists(foo.bar5) + + doAssert overloadExists(foo.bar1) + doAssert overloadExists(Foo().bar1) + + doAssert not compiles(nonexistant.bar1) + doAssert not compiles(overloadExists(nonexistant.bar1)) + doAssert not compiles(overloadExists(nonexistant().bar1)) + + block: # resolveSymbol doAssert resolveSymbol(fun8(1))(3) == fun8(3) inspect resolveSymbol(fun8) @@ -134,7 +164,6 @@ proc main()= import std/strutils import std/macros import std/macros as macrosAlias -import ./mresolve_overloads proc main2()= block: @@ -183,4 +212,5 @@ proc main2()= proc funDecl2(a: int) = discard main() +static: main() main2() From 2ce4bd45312aecd50624f397cfb8fddb01e1194f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 21 Mar 2020 20:58:05 -0700 Subject: [PATCH 11/17] _ --- compiler/semexprs.nim | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 12499cbd994ac..a779998cdcae8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2196,26 +2196,22 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = result.info = n.info result.typ = getSysType(c.graph, n.info, tyBool) -proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags, n1: PNode = nil): PNode = - let isTopLevel = n1 == nil - var n1 = n1 - if n1 == nil: +proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags, isTopLevel: bool): PNode = + var n = n + if isTopLevel: if n.len != 2: localError(c.config, n.info, "semOverloadResolve: got" & $n.len) return - n1 = n[1] - n1.flags.incl nfOverloadResolve - case n1.kind - of {nkIdent,nkDotExpr,nkAccQuoted} + nkCallKinds - {nkHiddenCallConv}: - # CHECKME - # let flags = flags + {efWantIterator} + n = n[1] + n.flags.incl nfOverloadResolve + if n.kind notin {nkIdent,nkDotExpr,nkAccQuoted} + nkCallKinds - {nkHiddenCallConv}: + localError(c.config, n.info, "expected routine, got " & $n.kind) + else: # so that it also works for iterators and allows nonexistant fields and procs let flags = flags + {efWantIterator, efNoUndeclared} - result = semExpr(c, n1, flags) + result = semExpr(c, n, flags) if result != nil: doAssert result.kind in {nkSym, nkClosedSymChoice}, $result.kind - else: - localError(c.config, n.info, "expected routine, got " & $n1.kind) if result == nil: if isTopLevel: result = newNodeIT(nkNilLit, n.info, getSysType(c.graph, n.info, tyNil)) @@ -2299,7 +2295,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = semCompiles(c, setMs(n, s), flags) of mOverloadResolve: markUsed(c, n.info, s) - result = semOverloadResolve(c, setMs(n, s), flags) + result = semOverloadResolve(c, setMs(n, s), flags, isTopLevel = true) of mIs: markUsed(c, n.info, s) result = semIs(c, setMs(n, s), flags) @@ -2813,7 +2809,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if result.kind == nkDotCall: result.transitionSonsKind(nkCall) if hasOverloadResolve: - result = semOverloadResolve(c, result, flags, result) + result = semOverloadResolve(c, result, flags, isTopLevel = false) else: result = semExpr(c, result, flags) elif result.kind == nkDotExpr and hasOverloadResolve: From 982af29f83d2f1954c0d043d3822e7752c6d36fb Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 21 Mar 2020 20:59:33 -0700 Subject: [PATCH 12/17] _ --- compiler/semcall.nim | 6 +----- compiler/semexprs.nim | 13 ++++--------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 0797e63e9eca2..f64fe3cb94682 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -520,11 +520,7 @@ proc semResolvedCall(c: PContext, x: TCandidate, markUsed(c, info, finalCallee) onUse(info, finalCallee) assert finalCallee.ast != nil - if nfOverloadResolve in n.flags: - # CHECKME: see if handling of `hasFauxMatch` is correct - let info2 = info - return newSymNode(finalCallee, info2) - + if nfOverloadResolve in n.flags: return newSymNode(finalCallee, info) if x.hasFauxMatch: result = x.call result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a779998cdcae8..93ad74c0ae82f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1367,16 +1367,11 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = var flags2 = {checkAmbiguity, checkUndeclared, checkModule} var s = qualifiedLookUp(c, n, flags2) - if nfOverloadResolve in n.flags and n.kind == nkDotExpr: var m = qualifiedLookUp(c, n[0], (flags2*{checkUndeclared})+{checkModule}) - if m != nil and m.kind == skModule: - # it's mymodule.someident - if s == nil: - return nil - else: - return symChoice(c, n, s, scClosed) - + if m != nil and m.kind == skModule: # got `mymodule.someident` + if s == nil: return nil + else: return symChoice(c, n, s, scClosed) if s != nil: if s.kind in OverloadableSyms: result = symChoice(c, n, s, scClosed) @@ -2191,7 +2186,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = # we replace this node by a 'true' or 'false' node: - if n.len != 2: return semDirectOp(c, n, flags) # why needed? + if n.len != 2: return semDirectOp(c, n, flags) result = newIntNode(nkIntLit, ord(tryExpr(c, n[1], flags) != nil)) result.info = n.info result.typ = getSysType(c.graph, n.info, tyBool) From 12feac39e2eacf7ffd8723dcb591fb70f70de809 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 21 Mar 2020 21:19:27 -0700 Subject: [PATCH 13/17] cleanups --- compiler/ast.nim | 1 - compiler/lookups.nim | 7 +-- compiler/semcall.nim | 8 +-- compiler/semdata.nim | 5 +- compiler/semexprs.nim | 81 +++++++++++++++++++++-------- compiler/vm.nim | 2 +- tests/magics/mresolve_overloads.nim | 6 +-- tests/magics/tresolve_overloads.nim | 30 +++++++---- 8 files changed, 95 insertions(+), 45 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 52b926db9a65c..e8d3d697efa52 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -495,7 +495,6 @@ type nfExecuteOnReload # A top-level statement that will be executed during reloads nfLastRead # this node is a last read nfFirstWrite# this node is a first write - nfOverloadResolve # return resolved `foo` in `foo(args)` TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~40) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 779ef80868d61..cd97c17c2f4a6 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -495,7 +495,7 @@ proc lookUp*(c: PContext, n: PNode): PSym = type TLookupFlag* = enum - checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields + checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields, checkOverloadResolve proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage} @@ -520,7 +520,8 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = if amb and checkAmbiguity in flags: errorUseQualifier(c, n.info, candidates) - if result == nil and checkUndeclared in flags: + # if result == nil and checkUndeclared in flags: + if result == nil and checkUndeclared in flags and checkOverloadResolve notin flags: result = errorUndeclaredIdentifierHint(c, n, ident) elif checkAmbiguity in flags and result != nil and amb: result = errorUseQualifier(c, n.info, result, amb) @@ -541,7 +542,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config) else: result = someSym(c.graph, m, ident).skipAlias(n, c.config) - if result == nil and checkUndeclared in flags and nfOverloadResolve notin n.flags: + if result == nil and checkUndeclared in flags and checkOverloadResolve notin flags: result = errorUndeclaredIdentifierHint(c, n[1], ident) elif n[1].kind == nkSym: result = n[1].sym diff --git a/compiler/semcall.nim b/compiler/semcall.nim index f64fe3cb94682..e329570552b83 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -410,7 +410,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode, pickBest(callOp) if overloadsState == csEmpty and result.state == csEmpty: - if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim + if {efNoUndeclared, efOverloadResolve} * flags == {}: + # for tests/pragmas/tcustom_pragma.nim # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident) localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f)) return @@ -520,7 +521,7 @@ proc semResolvedCall(c: PContext, x: TCandidate, markUsed(c, info, finalCallee) onUse(info, finalCallee) assert finalCallee.ast != nil - if nfOverloadResolve in n.flags: return newSymNode(finalCallee, info) + if efOverloadResolve in flags: return newSymNode(finalCallee, info) if x.hasFauxMatch: result = x.call result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) @@ -569,8 +570,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, filter: TSymKinds, flags: TExprFlags): PNode {.nosinks.} = var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) - template canError(): bool = - efNoUndeclared notin flags and nfOverloadResolve notin n.flags + template canError(): bool = {efNoUndeclared, efOverloadResolve} * flags == {} if r.state == csMatch: # this may be triggered, when the explain pragma is used if errors.len > 0: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index daf1265e87444..1a2d2adc7959b 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -69,9 +69,12 @@ type efWantStmt, efAllowStmt, efDetermineType, efExplain, efAllowDestructor, efWantValue, efOperand, efNoSemCheck, efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check, - efNoUndeclared + efNoUndeclared, # Use this if undeclared identifiers should not raise an error during # overload resolution. + efOverloadResolve, + # for `mOverloadResolve` evaluation + # return resolved `foo` in `foo(args)` TExprFlags* = set[TExprFlag] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 93ad74c0ae82f..b9601c93f61d0 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -64,8 +64,11 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.typ = errorType(c) proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode = + echo0b (flags, c.config$n.info) rejectEmptyNode(n) + echo0b () result = semExpr(c, n, flags+{efWantValue}) + echo0b () if result == nil: return errorNode(c, n) let @@ -866,7 +869,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags) if result != nil: - if nfOverloadResolve in n.flags: return + if efOverloadResolve in flags: return if result[0].kind != nkSym: internalError(c.config, "semOverloadedCallAnalyseEffects") return @@ -949,11 +952,20 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: n[0] = n0 else: + echo0b (flags, n[0].renderTree, n.renderTree, n.kind, n[0].kind) var flags2 = {efInCall} - if nfOverloadResolve in n.flags: - flags2.incl {efNoUndeclared} + if efOverloadResolve in flags and efOperand notin flags: flags2.incl efOverloadResolve n[0] = semExpr(c, n[0], flags2) - if n[0] == nil and nfOverloadResolve in n.flags: return nil + # n[0] = semExpr(c, n[0], {efInCall} + flags * {efOverloadResolve}) + # n[0] = semExpr(c, n[0], {efInCall}) + if efOverloadResolve in flags: + echo0b (flags, n[0] == nil, ) + if n[0] != nil: + echo0b n[0].kind + if n[0] == nil and efOverloadResolve in flags: + echo0b "errorNode" + return errorNode(c, n) + # return nil let t = n[0].typ if t != nil and t.kind in {tyVar, tyLent}: n[0] = newDeref(n[0]) @@ -1037,7 +1049,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = let nOrig = n.copyTree #semLazyOpAux(c, n) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) - if nfOverloadResolve in n.flags: return + if efOverloadResolve in flags: return if result != nil: result = afterCallActions(c, result, nOrig, flags) else: result = errorNode(c, n) @@ -1366,8 +1378,11 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n) var flags2 = {checkAmbiguity, checkUndeclared, checkModule} + if efOverloadResolve in flags: + flags2.incl checkOverloadResolve + # flags2.excl checkUndeclared # PRTEMP var s = qualifiedLookUp(c, n, flags2) - if nfOverloadResolve in n.flags and n.kind == nkDotExpr: + if efOverloadResolve in flags and n.kind == nkDotExpr: var m = qualifiedLookUp(c, n[0], (flags2*{checkUndeclared})+{checkModule}) if m != nil and m.kind == skModule: # got `mymodule.someident` if s == nil: return nil @@ -2193,29 +2208,47 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags, isTopLevel: bool): PNode = var n = n + echo0b (c.config$n.info, n.kind, flags, isTopLevel) if isTopLevel: if n.len != 2: localError(c.config, n.info, "semOverloadResolve: got" & $n.len) return n = n[1] - n.flags.incl nfOverloadResolve + let nKind = n.kind if n.kind notin {nkIdent,nkDotExpr,nkAccQuoted} + nkCallKinds - {nkHiddenCallConv}: localError(c.config, n.info, "expected routine, got " & $n.kind) - else: - # so that it also works for iterators and allows nonexistant fields and procs - let flags = flags + {efWantIterator, efNoUndeclared} - result = semExpr(c, n, flags) - if result != nil: - doAssert result.kind in {nkSym, nkClosedSymChoice}, $result.kind - if result == nil: + return + + # PRTEMP: remove efOperand stuff + # let flags = flags + {efWantIterator, efOverloadResolve} + if n.kind == nkDotExpr: + # let flags = flags + {efWantIterator, efOverloadResolve} - {efOperand} + # result = semExpr(c, n[0], flags) + # result = semExpr(c, n[0], flags) + n[0] = semExpr(c, n[0], flags) + let flags = flags + {efWantIterator, efOverloadResolve} - {efOperand} + result = semExpr(c, n, flags) + + echo0b (result == nil, ?.result.kind, nKind, flags) + # if result != nil and result.kind == nkEmpty and nKind == nkDotExpr: + when false: + if (result == nil or result.kind == nkEmpty) and nKind == nkDotExpr: + # localError() + # localError(c.config, n.info, "invalid expression for OverloadResolve: " & $n.renderTree) + localError(c.config, n.info, "invalid expression for OverloadResolve") + return result # PRTEMP + if result == nil or result.kind == nkEmpty: + # doAssert isTopLevel # PRTEMP if isTopLevel: result = newNodeIT(nkNilLit, n.info, getSysType(c.graph, n.info, tyNil)) - elif result.kind != nkSym: + elif result.kind == nkClosedSymChoice: # avoids degenerating symchoice to a sym let typ = newTypeS(tyTuple, c) let result0 = result result = newNodeIT(nkTupleConstr, n.info, typ) result.add result0 + else: + doAssert result.kind == nkSym, $result.kind proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = if n.len == 3: @@ -2741,11 +2774,16 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = {checkUndeclared, checkModule, checkPureEnumFields} else: {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} - if efNoUndeclared in flags: checks.excl checkUndeclared + # if efOverloadResolve in flags and efInCall notin flags: + if efOverloadResolve in flags and efInCall in flags: + # PRTEMP efOverloadResolve checkOverloadResolve + # checks.excl checkUndeclared + checks.incl checkOverloadResolve + echo0b (checks, flags) var s = qualifiedLookUp(c, n, checks) - if efNoUndeclared in flags and s == nil: return nil + if efOverloadResolve in flags and s == nil: return nil if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) - if nfOverloadResolve in n.flags: result = symChoice(c, n, s, scClosed) + if efOverloadResolve in flags: result = symChoice(c, n, s, scClosed) elif s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) @@ -2799,15 +2837,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkCharLit: if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyChar) of nkDotExpr: - let hasOverloadResolve = nfOverloadResolve in n.flags result = semFieldAccess(c, n, flags) if result.kind == nkDotCall: result.transitionSonsKind(nkCall) - if hasOverloadResolve: + if efOverloadResolve in flags: result = semOverloadResolve(c, result, flags, isTopLevel = false) else: result = semExpr(c, result, flags) - elif result.kind == nkDotExpr and hasOverloadResolve: + elif result.kind == nkDotExpr and efOverloadResolve in flags: result = result[1] of nkBind: message(c.config, n.info, warnDeprecated, "bind is deprecated") @@ -2829,7 +2866,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = checkMinSonsLen(n, 1, c.config) #when defined(nimsuggest): # if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n) - let mode = if {nfDotField, nfOverloadResolve} * n.flags != {}: {} else: {checkUndeclared} + let mode = if nfDotField in n.flags or efOverloadResolve in flags: {} else: {checkUndeclared} c.isAmbiguous = false var s = qualifiedLookUp(c, n[0], mode) if s != nil: diff --git a/compiler/vm.nim b/compiler/vm.nim index 088593fbc05d2..e3ba2994f537b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1123,7 +1123,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var a = regs[rb].node if a.kind == nkVarTy: a = a[0] if a.kind == nkSym: - regs[ra].node = if a.sym.ast.isNil: newNodeI(nkNilLit, a.sym.info) # preserve `info`, eg for module symbols + regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit) else: copyTree(a.sym.ast) regs[ra].node.flags.incl nfIsRef else: diff --git a/tests/magics/mresolve_overloads.nim b/tests/magics/mresolve_overloads.nim index ae0f559692444..a05a736ca4e62 100644 --- a/tests/magics/mresolve_overloads.nim +++ b/tests/magics/mresolve_overloads.nim @@ -1,6 +1,6 @@ -let foo1* = [1,2] ## c1 -var foo2* = "asdf" ## c2 -const foo3* = 'a' ## c3 +let mfoo1* = [1,2] ## c1 +var mfoo2* = "asdf" ## c2 +const mfoo3* = 'a' ## c3 proc `@@@`*(a: int) = discard proc `@@@`*(a: float) = discard diff --git a/tests/magics/tresolve_overloads.nim b/tests/magics/tresolve_overloads.nim index 9fcc5551ab5b3..364643ee25ee0 100644 --- a/tests/magics/tresolve_overloads.nim +++ b/tests/magics/tresolve_overloads.nim @@ -50,7 +50,9 @@ proc main()= doAssert overloadExists(fun4(1)) doAssert overloadExists(fun4(1.2)) doAssert not overloadExists(fun4()) - # doAssert not overloadExists(nonexistant(1)) # should we error with `Error: undeclared identifier: 'nonexistant'` ? A: probly should just return false, eg: imagine for: ` 1 @ 2` + doAssert not overloadExists(1 @@@ 2) + doAssert overloadExists(1 + 2) + doAssert not overloadExists('a' + 2.0) doAssert overloadExists(funDecl1(1)) doAssert not overloadExists(funDecl1(1.0)) @@ -92,12 +94,14 @@ proc main()= template bar5(a: Foo) = discard doAssert not declared(mresolve_overloads.nonexistant) - doAssert declared(mresolve_overloads.foo3) + doAssert declared(mresolve_overloads.mfoo3) doAssert not declared(foo.bar1) doAssert not overloadExists(mresolve_overloads.nonexistant) - doAssert overloadExists(mresolve_overloads.foo3) + doAssert overloadExists(mresolve_overloads.mfoo3) doAssert not overloadExists(nonexistant(foo)) + doAssert not overloadExists(nonexistant()) + doAssert not overloadExists(foo.nonexistant) doAssert compiles(Foo().bar1) doAssert compiles(Foo().bar1) @@ -116,8 +120,14 @@ proc main()= doAssert overloadExists(Foo().bar1) doAssert not compiles(nonexistant.bar1) - doAssert not compiles(overloadExists(nonexistant.bar1)) - doAssert not compiles(overloadExists(nonexistant().bar1)) + doAssert not compiles overloadExists(nonexistant.bar1) + doAssert not compiles overloadExists(nonexistant().mfoo1) + + doAssert not compiles overloadExists(nonexistant1().nonexistant2) + doAssert not compiles overloadExists(nonexistant().bar2) + doAssert not compiles overloadExists(nonexistant().bar1) + + # doAssert not overloadExists(nonexistant) # PRTEMP block: # resolveSymbol doAssert resolveSymbol(fun8(1))(3) == fun8(3) @@ -186,7 +196,7 @@ proc main2()= doAssert compiles resolveSymbol(system.compiles) inspect resolveSymbol(system.compiles) doAssert resolveSymbol(system.nonexistant) == nil - doAssert resolveSymbol(nonexistant) == nil + # doAssert resolveSymbol(nonexistant) == nil block: template bar1(): untyped = 12 @@ -199,10 +209,10 @@ proc main2()= inspect resolveSymbol(cint) inspect resolveSymbol(system.off) inspect resolveSymbol(newLit(true)) - inspect resolveSymbol(foo1) - inspect resolveSymbol(foo2) - inspect resolveSymbol(foo3) - inspect resolveSymbol(mresolve_overloads.foo3) + inspect resolveSymbol(mfoo1) + inspect resolveSymbol(mfoo2) + inspect resolveSymbol(mfoo3) + inspect resolveSymbol(mresolve_overloads.mfoo3) inspect resolveSymbol(macros.nnkCallKinds) ## module From aa0794fef3d66f084bba23680a6737174a597627 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 22 Mar 2020 01:48:19 -0700 Subject: [PATCH 14/17] all works --- compiler/lookups.nim | 1 - compiler/semdata.nim | 3 +- compiler/semexprs.nim | 53 ++++------------------------- tests/magics/mresolves.nim | 2 +- tests/magics/tresolve_overloads.nim | 17 +++++++-- 5 files changed, 23 insertions(+), 53 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index cd97c17c2f4a6..ed096b0f06fa8 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -520,7 +520,6 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = if amb and checkAmbiguity in flags: errorUseQualifier(c, n.info, candidates) - # if result == nil and checkUndeclared in flags: if result == nil and checkUndeclared in flags and checkOverloadResolve notin flags: result = errorUndeclaredIdentifierHint(c, n, ident) elif checkAmbiguity in flags and result != nil and amb: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 1a2d2adc7959b..a2470832aa62d 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -73,8 +73,7 @@ type # Use this if undeclared identifiers should not raise an error during # overload resolution. efOverloadResolve, - # for `mOverloadResolve` evaluation - # return resolved `foo` in `foo(args)` + # for `mOverloadResolve` evaluation, resolves `foo` in `foo(args)` TExprFlags* = set[TExprFlag] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b9601c93f61d0..4c2da73d59b49 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -64,11 +64,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.typ = errorType(c) proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode = - echo0b (flags, c.config$n.info) rejectEmptyNode(n) - echo0b () result = semExpr(c, n, flags+{efWantValue}) - echo0b () if result == nil: return errorNode(c, n) let @@ -952,20 +949,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: n[0] = n0 else: - echo0b (flags, n[0].renderTree, n.renderTree, n.kind, n[0].kind) - var flags2 = {efInCall} - if efOverloadResolve in flags and efOperand notin flags: flags2.incl efOverloadResolve - n[0] = semExpr(c, n[0], flags2) - # n[0] = semExpr(c, n[0], {efInCall} + flags * {efOverloadResolve}) - # n[0] = semExpr(c, n[0], {efInCall}) - if efOverloadResolve in flags: - echo0b (flags, n[0] == nil, ) - if n[0] != nil: - echo0b n[0].kind - if n[0] == nil and efOverloadResolve in flags: - echo0b "errorNode" - return errorNode(c, n) - # return nil + n[0] = semExpr(c, n[0], {efInCall} + flags * {efOverloadResolve}) + if n[0] == nil and efOverloadResolve in flags: return errorNode(c, n) let t = n[0].typ if t != nil and t.kind in {tyVar, tyLent}: n[0] = newDeref(n[0]) @@ -1378,9 +1363,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n) var flags2 = {checkAmbiguity, checkUndeclared, checkModule} - if efOverloadResolve in flags: - flags2.incl checkOverloadResolve - # flags2.excl checkUndeclared # PRTEMP + if efOverloadResolve in flags: flags2.incl checkOverloadResolve var s = qualifiedLookUp(c, n, flags2) if efOverloadResolve in flags and n.kind == nkDotExpr: var m = qualifiedLookUp(c, n[0], (flags2*{checkUndeclared})+{checkModule}) @@ -2208,37 +2191,20 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semOverloadResolve(c: PContext, n: PNode, flags: TExprFlags, isTopLevel: bool): PNode = var n = n - echo0b (c.config$n.info, n.kind, flags, isTopLevel) if isTopLevel: if n.len != 2: localError(c.config, n.info, "semOverloadResolve: got" & $n.len) return n = n[1] - let nKind = n.kind if n.kind notin {nkIdent,nkDotExpr,nkAccQuoted} + nkCallKinds - {nkHiddenCallConv}: localError(c.config, n.info, "expected routine, got " & $n.kind) - return - - # PRTEMP: remove efOperand stuff - # let flags = flags + {efWantIterator, efOverloadResolve} + return errorNode(c, n) if n.kind == nkDotExpr: - # let flags = flags + {efWantIterator, efOverloadResolve} - {efOperand} - # result = semExpr(c, n[0], flags) - # result = semExpr(c, n[0], flags) + # so that this doesn't compile: `overloadExists(nonexistant().foo)` n[0] = semExpr(c, n[0], flags) - let flags = flags + {efWantIterator, efOverloadResolve} - {efOperand} + let flags = flags + {efWantIterator, efOverloadResolve} result = semExpr(c, n, flags) - - echo0b (result == nil, ?.result.kind, nKind, flags) - # if result != nil and result.kind == nkEmpty and nKind == nkDotExpr: - when false: - if (result == nil or result.kind == nkEmpty) and nKind == nkDotExpr: - # localError() - # localError(c.config, n.info, "invalid expression for OverloadResolve: " & $n.renderTree) - localError(c.config, n.info, "invalid expression for OverloadResolve") - return result # PRTEMP if result == nil or result.kind == nkEmpty: - # doAssert isTopLevel # PRTEMP if isTopLevel: result = newNodeIT(nkNilLit, n.info, getSysType(c.graph, n.info, tyNil)) elif result.kind == nkClosedSymChoice: @@ -2774,12 +2740,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = {checkUndeclared, checkModule, checkPureEnumFields} else: {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} - # if efOverloadResolve in flags and efInCall notin flags: - if efOverloadResolve in flags and efInCall in flags: - # PRTEMP efOverloadResolve checkOverloadResolve - # checks.excl checkUndeclared - checks.incl checkOverloadResolve - echo0b (checks, flags) + if efOverloadResolve in flags: checks.incl checkOverloadResolve var s = qualifiedLookUp(c, n, checks) if efOverloadResolve in flags and s == nil: return nil if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) diff --git a/tests/magics/mresolves.nim b/tests/magics/mresolves.nim index 47910642dc1c8..38c98a3174084 100644 --- a/tests/magics/mresolves.nim +++ b/tests/magics/mresolves.nim @@ -1,6 +1,6 @@ import std/macros -macro overloadExistsImpl(x: typed): bool = +macro overloadExistsImpl(x: typed): bool = newLit(x != nil) template overloadExists*(a: untyped): bool = diff --git a/tests/magics/tresolve_overloads.nim b/tests/magics/tresolve_overloads.nim index 364643ee25ee0..2658e95c896ca 100644 --- a/tests/magics/tresolve_overloads.nim +++ b/tests/magics/tresolve_overloads.nim @@ -122,12 +122,23 @@ proc main()= doAssert not compiles(nonexistant.bar1) doAssert not compiles overloadExists(nonexistant.bar1) doAssert not compiles overloadExists(nonexistant().mfoo1) - doAssert not compiles overloadExists(nonexistant1().nonexistant2) doAssert not compiles overloadExists(nonexistant().bar2) doAssert not compiles overloadExists(nonexistant().bar1) - # doAssert not overloadExists(nonexistant) # PRTEMP + doAssert declared(mresolve_overloads) + doAssert declared(fun7) + doAssert declared(foo) + doAssert declared(`bar5`) + doAssert declared(bar5) + doAssert declared(`system`) + doAssert not declared(nonexistant) + doAssert not overloadExists(nonexistant) + doAssert not overloadExists(`nonexistant`) + doAssert overloadExists(system) + doAssert overloadExists(`system`) + doAssert overloadExists(`bar5`) + doAssert overloadExists(bar5) block: # resolveSymbol doAssert resolveSymbol(fun8(1))(3) == fun8(3) @@ -196,7 +207,7 @@ proc main2()= doAssert compiles resolveSymbol(system.compiles) inspect resolveSymbol(system.compiles) doAssert resolveSymbol(system.nonexistant) == nil - # doAssert resolveSymbol(nonexistant) == nil + doAssert resolveSymbol(nonexistant) == nil block: template bar1(): untyped = 12 From 6ba4f6708626ebce4b667aad8d8399c6ed2093c7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 22 Mar 2020 05:25:47 -0700 Subject: [PATCH 15/17] add more tests --- tests/magics/tresolve_overloads.nim | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/magics/tresolve_overloads.nim b/tests/magics/tresolve_overloads.nim index 2658e95c896ca..abe44dbd0684d 100644 --- a/tests/magics/tresolve_overloads.nim +++ b/tests/magics/tresolve_overloads.nim @@ -43,6 +43,10 @@ macro fun8(c: static bool): untyped = discard proc fun8(d: var int) = d.inc +proc fun10[T: seq](a: T): auto = (a,) +proc fun10[T: int|float](a: T): auto = (a,) +template fun11(a: untyped): auto = (a,) + proc main()= block: # overloadExists with nkCall doAssert overloadExists(fun4(1)) @@ -80,6 +84,26 @@ proc main()= doAssert overloadExists(fun7(1)) doAssert not overloadExists(fun7()) + # generics + doAssert overloadExists(fun10(@[1])) + doAssert overloadExists(fun10(1.2)) + doAssert not overloadExists(fun10("foo")) + doAssert not overloadExists(fun10('a')) + template fun10(a: char): auto = (a,) + doAssert overloadExists(fun10('a')) + doAssert not overloadExists(fun11()) + doAssert overloadExists(fun11(1 + 'a')) + + # $ + doAssert overloadExists($12) + doAssert not overloadExists($main) + # echo + doAssert overloadExists(echo 12) + doAssert overloadExists(echo ()) + doAssert overloadExists(echo()) + doAssert not overloadExists(echo main) + doAssert not overloadExists(echo(main)) + block: # overloadExists with nkCall dot accesors type Foo = object bar1: int From ee8bfa8b8814cb3d3d1d9df1b98676112ceaeece Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 24 Mar 2020 17:59:49 -0700 Subject: [PATCH 16/17] _ --- tests/magics/mresolves.nim | 59 +++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/tests/magics/mresolves.nim b/tests/magics/mresolves.nim index 38c98a3174084..849edbbdd27cf 100644 --- a/tests/magics/mresolves.nim +++ b/tests/magics/mresolves.nim @@ -32,7 +32,7 @@ proc inspectImpl*(s: var string, a: NimNode, resolveLet: bool) = for i, ai in a20: a2.add if i notin [6]: ai else: newEmptyNode() s.add a2.lineInfoObj.toStr & " " & a2.repr - else: error($a.kind, a) + else: error($a.kind, a) # Error: nnkNilLit when couldn't resolve macro inspect*(a: typed, resolveLet: static bool = false): untyped = var a = a @@ -44,3 +44,60 @@ macro inspect*(a: typed, resolveLet: static bool = false): untyped = inspectImpl(s, a, resolveLet) when defined(nimTestsResolvesDebug): echo s + +template inspect2*(a: untyped): untyped = inspect(resolveSymbol(a)) + +macro fieldExistsImpl(a: typed): bool = + newLit(a.symKind == nskField) + +template fieldExists*(a: untyped): untyped = fieldExistsImpl(resolveSymbol(a)) + +macro canImportImpl(a: typed): bool = + newLit(a.symKind == nskModule) + +# can't work like that... +template canImport*(a: untyped): untyped = canImportImpl(resolveSymbol(a)) + +macro inspect3*(a: typed): untyped = + echo (a.repr, a.kind, a.typeKind) + echo a.symKind + var a2 = a.getImpl + echo a2.kind + # echo a.sym.kind + +# macro getSymImpl(a: typed): NimSym = +type SymWrap = object + s: NimSym +type SymWrap2 = object + s: int +type SymWrap3[T] = object + s: NimSym +# macro getSymImpl(a: typed): NimSym = + +type SymWrap4*[sym] = object + +# type SymWrap4b*[sym] = object +# sym2: sym + +type SymWrap4b* = object + # sym2: T + sym2: NimSym + +macro getSymImpl(a: typed): untyped = + # NimSym + let x = a.symbol + result = quote do: + SymWrap4[`x`] + +template getSym*(a: untyped): untyped = getSymImpl(resolveSymbol(a)) + +type SymInt* = distinct int +macro getSymImpl2(a: typed): untyped = + # NimSym + let x = cast[int](a.symbol) + result = quote do: + # SymWrap4b[`x`](sym2: `x`) + # SymWrap4b(sym2: `x`) + `x`.SymInt + +template getSym2*(a: untyped): untyped = getSymImpl2(resolveSymbol(a)) From 52715b39dac56ace36c3701a1bf7f8b8eee331ac Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 13 Apr 2021 12:11:14 -0700 Subject: [PATCH 17/17] PRTEMP --- compiler/semexprs.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4c2da73d59b49..23ff47489d0b5 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2863,8 +2863,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semDirectOp(c, n, flags) else: result = semIndirectOp(c, n, flags) - - if nfDefaultRefsParam in result.flags: + if result == nil: + # dbg "D20210413T120912" # PRTEMP + discard + elif nfDefaultRefsParam in result.flags: + # if nfDefaultRefsParam in result.flags: result = result.copyTree #XXX: Figure out what causes default param nodes to be shared.. (sigmatch bug?) # We've found a default value that references another param. # See the notes in `hoistParamsUsedInDefault` for more details.