diff --git a/changelog.md b/changelog.md index 1eda82297de3b..05a31aace7165 100644 --- a/changelog.md +++ b/changelog.md @@ -140,7 +140,7 @@ initial size must be a power of two - this is done internally. Proc `rightSize` for Tables and HashSets is deprecated, as it is not needed anymore. - +- Add `decls.byLent` to turn an argument into a let param, useful for overload resolution. ## Language changes - In the newruntime it is now allowed to assign to the discriminator field diff --git a/compiler/options.nim b/compiler/options.nim index 247f35419faee..ce3bc937a0eb2 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -13,6 +13,7 @@ import from terminal import isatty from times import utc, fromUnix, local, getTime, format, DateTime +from std/decls import byLent const hasTinyCBackend* = defined(tinyc) @@ -619,12 +620,6 @@ proc shortenDir*(conf: ConfigRef; dir: string): string {. return substr(dir, prefix.len) result = dir -proc removeTrailingDirSep*(path: string): string = - if (path.len > 0) and (path[^1] == DirSep): - result = substr(path, 0, path.len - 2) - else: - result = path - proc disableNimblePath*(conf: ConfigRef) = incl conf.globalOptions, optNoNimblePath conf.lazyPaths.setLen(0) @@ -653,7 +648,7 @@ proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = (if isDefined(conf, "release") or isDefined(conf, "danger"): "_r" else: "_d")) proc pathSubs*(conf: ConfigRef; p, config: string): string = - let home = removeTrailingDirSep(os.getHomeDir()) + let home = os.getHomeDir().normalizePathEnd result = unixToNativePath(p % [ "nim", getPrefixDir(conf).string, "lib", conf.libpath.string, @@ -668,7 +663,7 @@ iterator nimbleSubs*(conf: ConfigRef; p: string): string = let pl = p.toLowerAscii if "$nimblepath" in pl or "$nimbledir" in pl: for i in countdown(conf.nimblePaths.len-1, 0): - let nimblePath = removeTrailingDirSep(conf.nimblePaths[i].string) + let nimblePath = conf.nimblePaths[i].string.byLent.normalizePathEnd yield p % ["nimblepath", nimblePath, "nimbledir", nimblePath] else: yield p diff --git a/lib/std/decls.nim b/lib/std/decls.nim index dd7d19da7d563..e04f594680a4e 100644 --- a/lib/std/decls.nim +++ b/lib/std/decls.nim @@ -1,5 +1,9 @@ -# see `semLowerLetVarCustomPragma` for compiler support that enables these -# lowerings +#[ +keep this module dependency-light to be usable in low level modules. + +see `semLowerLetVarCustomPragma` for compiler support that enables these +lowerings +]# template byaddr*(lhs, typ, ex) = ## Allows a syntax for lvalue reference, exact analog to @@ -17,3 +21,15 @@ template byaddr*(lhs, typ, ex) = else: let tmp: ptr typ = addr(ex) template lhs: untyped = tmp[] + +proc byLent*[T](a: var T): lent T {.inline.} = + ## Transforms `a` into a let param without copying; this is useful for overload + ## resolution + runnableExamples: + proc fn(a: int): int = result = a*2 + proc fn(a: var int) = a = a*2 + var x = 3 + # x = fn(x) # would give: Error: expression 'fn(x)' has no type (or is ambiguous) + x = fn(x.byLent) # works + doAssert x == 3*2 + result = a # just `a` hits: bug #14420 diff --git a/tests/js/taddr.nim b/tests/js/taddr.nim index 9c45621a8dced..b815c08daf904 100644 --- a/tests/js/taddr.nim +++ b/tests/js/taddr.nim @@ -81,29 +81,36 @@ doAssert(someGlobal == 10) block: # issue #14576 # lots of these used to give: Error: internal error: genAddr: 2 - proc byLent[T](a: T): lent T = a + proc byLentAny[T](a: T): lent T = a + proc byLentVar[T](a: var T): lent T = a # see also decls.byLent proc byPtr[T](a: T): ptr T = a.unsafeAddr block: let a = (10,11) - let (x,y) = byLent(a) + let (x,y) = byLentAny(a) + doAssert (x,y) == a + block: + var a = (10,11) + let (x,y) = byLentAny(a) doAssert (x,y) == a block: - when defined(c) and defined(release): - # bug; pending https://github.com/nim-lang/Nim/issues/14578 - discard - else: - let a = 10 - doAssert byLent(a) == 10 - let a2 = byLent(a) - doAssert a2 == 10 + let a = 10 + doAssert byLentAny(a) == 10 + let a2 = byLentAny(a) + doAssert a2 == 10 block: let a = [11,12] - doAssert byLent(a) == [11,12] + doAssert byLentAny(a) == [11,12] let a2 = (11,) - doAssert byLent(a2) == (11,) + doAssert byLentAny(a2) == (11,) + + block: + var a = [11,12] + doAssert byLentVar(a) == [11,12] + var a2 = (11,) + doAssert byLentVar(a2) == (11,) block: when defined(c) and defined(release): @@ -117,9 +124,9 @@ block: doAssert byPtr(a3)[] == 14 block: - proc byLent2[T](a: seq[T]): lent T = a[1] + proc byLentElem[T](a: seq[T]): lent T = a[1] var a = @[20,21,22] - doAssert byLent2(a) == 21 + doAssert byLentElem(a) == 21 block: # sanity checks proc bar[T](a: var T): var T = a @@ -134,6 +141,6 @@ block: bar(a2).inc doAssert a2 == 13 - block: # xxx: bug this doesn't work + block: # pending bug #14877 when false: proc byLent2[T](a: T): lent type(a[0]) = a[0] diff --git a/tests/stdlib/tdecls.nim b/tests/stdlib/tdecls.nim index 3567639e05e96..ec32fff6dbff6 100644 --- a/tests/stdlib/tdecls.nim +++ b/tests/stdlib/tdecls.nim @@ -1,6 +1,6 @@ import std/decls -block: +block: # byaddr var s = @[10,11,12] var a {.byaddr.} = s[0] a+=100 @@ -34,23 +34,9 @@ block: doAssert compiles(block: var b2 {.byaddr.}: int = s[2]) -## We can define custom pragmas in user code -template byUnsafeAddr(lhs, typ, expr) = - when typ is type(nil): - let tmp = unsafeAddr(expr) - else: - let tmp: ptr typ = unsafeAddr(expr) - template lhs: untyped = tmp[] - -block: - let s = @["foo", "bar"] - let a {.byUnsafeAddr.} = s[0] - doAssert a == "foo" - doAssert a[0].unsafeAddr == s[0][0].unsafeAddr - -block: # nkAccQuoted - # shows using a keyword, which requires nkAccQuoted - template `cast`(lhs, typ, expr) = +block: # custom var pragmas + ## We can define custom pragmas in user code + template byUnsafeAddr(lhs, typ, expr) = when typ is type(nil): let tmp = unsafeAddr(expr) else: @@ -59,12 +45,34 @@ block: # nkAccQuoted block: let s = @["foo", "bar"] - let a {.`byUnsafeAddr`.} = s[0] + let a {.byUnsafeAddr.} = s[0] doAssert a == "foo" doAssert a[0].unsafeAddr == s[0][0].unsafeAddr - block: - let s = @["foo", "bar"] - let a {.`cast`.} = s[0] - doAssert a == "foo" - doAssert a[0].unsafeAddr == s[0][0].unsafeAddr + block: # nkAccQuoted + # shows using a keyword, which requires nkAccQuoted + template `cast`(lhs, typ, expr) = + when typ is type(nil): + let tmp = unsafeAddr(expr) + else: + let tmp: ptr typ = unsafeAddr(expr) + template lhs: untyped = tmp[] + + block: + let s = @["foo", "bar"] + let a {.`byUnsafeAddr`.} = s[0] + doAssert a == "foo" + doAssert a[0].unsafeAddr == s[0][0].unsafeAddr + + block: + let s = @["foo", "bar"] + let a {.`cast`.} = s[0] + doAssert a == "foo" + doAssert a[0].unsafeAddr == s[0][0].unsafeAddr + +block: # byLent + proc fn(a: int): int = result = a*2 + proc fn(a: var int) = a = a*5 + var x = 3 + x = fn(x.byLent) + doAssert x == 3*2