mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 01:14:41 +00:00
fixes #23200, fixes #18866 #21065 made it so `auto` proc return types remained as `tyAnything` and not turned to `tyUntyped`. This had the side effect that anything previously bound to `tyAnything` in the proc type match was then bound to the proc return type, which is wrong since we don't know the proc return type even if we know the expected parameter types (`tyUntyped` also [does not care about its previous bindings in `typeRel`](ab4278d217/compiler/sigmatch.nim (L1059-L1061)) maybe for this reason). Now we mark `tyAnything` return types for routines as `tfRetType` [as done for other meta return types](18b5fb256d/compiler/semtypes.nim (L1451)), and ignore bindings to `tyAnything` + `tfRetType` types in `semtypinst`. On top of this, we reset the type relation in `paramTypesMatch` only after creating the instantiation (instead of trusting `isInferred`/`isInferredConvertible` before creating the instantiation), using the same mechanism that `isBothMetaConvertible` uses. This fixes the issues as well as making the disabled t15386_2 test introduced in #21065 work. As seen in the changes for the other tests, the error messages give an obscure `proc (a: GenericParam): auto` now, but it does give the correct error that the overload doesn't match instead of matching the overload pre-emptively and expecting a specific return type. tsugar had to be changed due to #16906, which is the problem where `void` is not inferred in the case where `result` was never touched.
311 lines
8.2 KiB
Nim
311 lines
8.2 KiB
Nim
discard """
|
|
targets: "c js"
|
|
matrix: "--mm:refc; --mm:orc"
|
|
output: '''
|
|
x + y = 30
|
|
'''
|
|
"""
|
|
import std/[sugar, algorithm, random, sets, tables, strutils, sequtils]
|
|
import std/[syncio, assertions]
|
|
|
|
type # for capture test, ref #20679
|
|
FooCapture = ref object
|
|
x: int
|
|
|
|
proc mainProc() =
|
|
block: # bug #16967
|
|
var s = newSeq[proc (): int](5)
|
|
{.push exportc.}
|
|
proc bar() =
|
|
for i in 0 ..< s.len:
|
|
let foo = i + 1
|
|
capture foo:
|
|
s[i] = proc(): int = foo
|
|
{.pop.}
|
|
|
|
bar()
|
|
|
|
for i, p in s.pairs:
|
|
let foo = i + 1
|
|
doAssert p() == foo
|
|
|
|
template main() =
|
|
block: # `=>`
|
|
block:
|
|
let f1 = () => 42
|
|
doAssert f1() == 42
|
|
|
|
let f2 = (x: int) => x + 1
|
|
doAssert f2(42) == 43
|
|
|
|
let f3 = (x, y: int) => x + y
|
|
doAssert f3(1, 2) == 3
|
|
|
|
var x = 0
|
|
let f4 = () => (x = 12)
|
|
f4()
|
|
doAssert x == 12
|
|
|
|
let f5 = () => (discard) # simplest proc that returns void
|
|
f5()
|
|
|
|
block:
|
|
proc call1(f: () -> int): int = f()
|
|
doAssert call1(() => 12) == 12
|
|
|
|
proc call2(f: int -> int): int = f(42)
|
|
doAssert call2(x => x) == 42
|
|
doAssert call2((x) => x) == 42
|
|
doAssert call2((x: int) => x) == 42
|
|
|
|
proc call3(f: (int, int) -> int): int = f(1, 2)
|
|
doAssert call3((x, y) => x + y) == 3
|
|
doAssert call3((x, y: int) => x + y) == 3
|
|
doAssert call3((x: int, y: int) => x + y) == 3
|
|
|
|
var a = 0
|
|
proc call4(f: int -> void) = f(42)
|
|
call4((x: int) => (a = x))
|
|
doAssert a == 42
|
|
|
|
proc call5(f: (int {.noSideEffect.} -> int)): int = f(42)
|
|
doAssert call5(x {.noSideEffect.} => x + 1) == 43
|
|
|
|
block: # `->`
|
|
doAssert $(() -> int) == "proc (): int{.closure.}"
|
|
doAssert $(float -> int) == "proc (i0: float): int{.closure.}"
|
|
doAssert $((float) -> int) == "proc (i0: float): int{.closure.}"
|
|
doAssert $((float, bool) -> int) == "proc (i0: float, i1: bool): int{.closure.}"
|
|
|
|
doAssert $(() -> void) == "proc (){.closure.}"
|
|
doAssert $(float -> void) == "proc (i0: float){.closure.}"
|
|
doAssert $((float) -> void) == "proc (i0: float){.closure.}"
|
|
doAssert $((float, bool) -> void) == "proc (i0: float, i1: bool){.closure.}"
|
|
|
|
doAssert $(() {.inline.} -> int) == "proc (): int{.inline.}"
|
|
doAssert $(float {.inline.} -> int) == "proc (i0: float): int{.inline.}"
|
|
doAssert $((float) {.inline.} -> int) == "proc (i0: float): int{.inline.}"
|
|
doAssert $((float, bool) {.inline.} -> int) == "proc (i0: float, i1: bool): int{.inline.}"
|
|
|
|
block: # capture
|
|
var closure1: () -> int
|
|
for i in 0 .. 10:
|
|
if i == 5:
|
|
capture i:
|
|
closure1 = () => i
|
|
doAssert closure1() == 5
|
|
|
|
var closure2: () -> (int, int)
|
|
for i in 0 .. 10:
|
|
for j in 0 .. 10:
|
|
if i == 5 and j == 3:
|
|
capture i, j:
|
|
closure2 = () => (i, j)
|
|
doAssert closure2() == (5, 3)
|
|
|
|
block: # issue #20679
|
|
# this should compile. Previously was broken as `var int` is an `nnkHiddenDeref`
|
|
# which was not handled correctly
|
|
|
|
block:
|
|
var x = 5
|
|
var s1 = newSeq[proc (): int](2)
|
|
proc function(data: var int) =
|
|
for i in 0 ..< 2:
|
|
data = (i+1) * data
|
|
capture data:
|
|
s1[i] = proc(): int = data
|
|
function(x)
|
|
doAssert s1[0]() == 5
|
|
doAssert s1[1]() == 10
|
|
|
|
|
|
block:
|
|
var y = @[5, 10]
|
|
var s2 = newSeq[proc (): seq[int]](2)
|
|
proc functionS(data: var seq[int]) =
|
|
for i in 0 ..< 2:
|
|
data.add (i+1) * 5
|
|
capture data:
|
|
s2[i] = proc(): seq[int] = data
|
|
functionS(y)
|
|
doAssert s2[0]() == @[5, 10, 5]
|
|
doAssert s2[1]() == @[5, 10, 5, 10]
|
|
|
|
|
|
template typeT(typ, val: untyped): untyped =
|
|
var x = val
|
|
var s = newSeq[proc (): typ](2)
|
|
|
|
proc functionT[T](data: var T) =
|
|
for i in 0 ..< 2:
|
|
if i == 1:
|
|
data = default(T)
|
|
capture data:
|
|
s[i] = proc (): T = data
|
|
|
|
functionT(x)
|
|
doAssert s[0]() == val
|
|
doAssert s[1]() == x # == default
|
|
doAssert s[1]() == default(typ)
|
|
|
|
block:
|
|
var x = 1.1
|
|
typeT(float, x)
|
|
block:
|
|
var x = "hello"
|
|
typeT(string, x)
|
|
block:
|
|
var f = FooCapture(x: 5)
|
|
typeT(FooCapture, f)
|
|
|
|
block: # dup
|
|
block dup_with_field:
|
|
type
|
|
Foo = object
|
|
col, pos: int
|
|
name: string
|
|
|
|
proc inc_col(foo: var Foo) = inc(foo.col)
|
|
proc inc_pos(foo: var Foo) = inc(foo.pos)
|
|
proc name_append(foo: var Foo, s: string) = foo.name &= s
|
|
|
|
let a = Foo(col: 1, pos: 2, name: "foo")
|
|
block:
|
|
let b = a.dup(inc_col, inc_pos):
|
|
_.pos = 3
|
|
name_append("bar")
|
|
inc_pos
|
|
|
|
doAssert(b == Foo(col: 2, pos: 4, name: "foobar"))
|
|
|
|
block:
|
|
let b = a.dup(inc_col, pos = 3, name = "bar"):
|
|
name_append("bar")
|
|
inc_pos
|
|
|
|
doAssert(b == Foo(col: 2, pos: 4, name: "barbar"))
|
|
|
|
block:
|
|
var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
doAssert dup(a, sort(_)) == sorted(a)
|
|
doAssert a.dup(sort) == sorted(a)
|
|
# Chaining:
|
|
var aCopy = a
|
|
aCopy.insert(10)
|
|
doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy)
|
|
|
|
block:
|
|
when nimvm: discard
|
|
else:
|
|
const b = @[0, 1, 2]
|
|
discard b.dup shuffle()
|
|
doAssert b[0] == 0
|
|
doAssert b[1] == 1
|
|
|
|
block: # collect
|
|
let data = @["bird", "word"] # if this gets stuck in your head, its not my fault
|
|
|
|
doAssert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"]
|
|
doAssert collect(initTable(2), for (i, d) in data.pairs: {i: d}) ==
|
|
{0: "bird", 1: "word"}.toTable
|
|
doAssert collect(initHashSet(), for d in data.items: {d}) == data.toHashSet
|
|
|
|
block:
|
|
let x = collect(newSeqOfCap(4)):
|
|
for (i, d) in data.pairs:
|
|
if i mod 2 == 0: d
|
|
doAssert x == @["bird"]
|
|
|
|
block: # bug #12874
|
|
let bug = collect(
|
|
newSeq,
|
|
for (i, d) in data.pairs:(
|
|
block:
|
|
if i mod 2 == 0:
|
|
d
|
|
else:
|
|
d & d
|
|
)
|
|
)
|
|
doAssert bug == @["bird", "wordword"]
|
|
|
|
block:
|
|
let y = collect(newSeq):
|
|
for (i, d) in data.pairs:
|
|
try: parseInt(d) except: 0
|
|
doAssert y == @[0, 0]
|
|
|
|
block:
|
|
let z = collect(newSeq):
|
|
for (i, d) in data.pairs:
|
|
case d
|
|
of "bird": "word"
|
|
else: d
|
|
doAssert z == @["word", "word"]
|
|
|
|
block:
|
|
proc tforum(): seq[int] =
|
|
collect(newSeq):
|
|
for y in 0..10:
|
|
if y mod 5 == 2:
|
|
for x in 0..y:
|
|
x
|
|
doAssert tforum() == @[0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7]
|
|
|
|
block:
|
|
let x = collect:
|
|
for d in data.items:
|
|
when d is int: "word"
|
|
else: d
|
|
doAssert x == @["bird", "word"]
|
|
|
|
block:
|
|
doAssert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")]
|
|
doAssert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0]
|
|
doAssert collect(for (i, d) in pairs(data): {i: d}) ==
|
|
{1: "word", 0: "bird"}.toTable
|
|
doAssert collect(for d in data.items: {d}) == data.toHashSet
|
|
|
|
block: # bug #14332
|
|
template foo =
|
|
discard collect(newSeq, for i in 1..3: i)
|
|
foo()
|
|
|
|
block: # dump
|
|
# symbols in templates are gensym'd
|
|
let
|
|
x {.inject.} = 10
|
|
y {.inject.} = 20
|
|
dump(x + y) # x + y = 30
|
|
|
|
block: # dumpToString
|
|
template square(x): untyped = x * x
|
|
let x {.inject.} = 10
|
|
doAssert dumpToString(square(x)) == "square(x): x * x = 100"
|
|
let s = dumpToString(doAssert 1+1 == 2)
|
|
doAssert "failedAssertImpl" in s
|
|
let s2 = dumpToString:
|
|
doAssertRaises(AssertionDefect): doAssert false
|
|
doAssert "except AssertionDefect" in s2
|
|
|
|
block: # bug #20704
|
|
proc test() =
|
|
var xs, ys: seq[int]
|
|
for i in 0..5:
|
|
xs.add(i)
|
|
|
|
xs.apply(proc (d: auto) = ys.add(d))
|
|
# ^ can be turned into d => ys.add(d) when we can infer void return type, #16906
|
|
doAssert ys == @[0, 1, 2, 3, 4, 5]
|
|
|
|
test()
|
|
|
|
mainProc()
|
|
|
|
when not defined(js): # TODO fixme JS VM
|
|
static:
|
|
main()
|
|
|
|
main()
|