diff options
Diffstat (limited to 'tests/template')
77 files changed, 3849 insertions, 0 deletions
diff --git a/tests/template/annotate.nim b/tests/template/annotate.nim new file mode 100644 index 000000000..a7e2f8fdb --- /dev/null +++ b/tests/template/annotate.nim @@ -0,0 +1,113 @@ +import macros, parseutils + +# Generate tags +macro make(names: untyped{nkBracket}): untyped = + result = newStmtList() + + for i in 0 .. names.len-1: + result.add newProc( + name = ident($names[i]).postfix("*"), + params = [ + ident("string"), + newIdentDefs( + ident("content"), + ident("string") + ) + ], + body = newStmtList( + parseStmt("reindent(content)") + ) + ) + + +iterator lines(value: string): string = + var i = 0 + while i < value.len: + var line: string + inc(i, value.parseUntil(line, 0x0A.char, i) + 1) + yield line + + +proc reindent*(value: string, preset_indent = 0): string = + var indent = -1 + + # Detect indentation! + for ln in lines(value): + var read = ln.skipWhitespace() + + # If the line is empty, ignore it for indentation + if read == ln.len: continue + + indent = if indent < 0: read + else: min(indent, read) + + # Create a precursor indent as-needed + var precursor = newString(0) + for i in 1 .. preset_indent: + precursor.add(' ') + + # Re-indent + result = newString(0) + + for ln in lines(value): + var value = ln.substr(indent) + + result.add(precursor) + + if value.len > 0: + result.add(value) + result.add(0x0A.char) + + return result + + +#Define tags +make([ html, xml, glsl, js, css, rst ]) + + +when isMainModule: + ## Test tags + + const script = js""" + var x = 5; + console.log(x.toString()); + """ + + const styles = css""" + .someRule { + width: 500px; + } + """ + + const body = html""" + <ul> + <li>1</li> + <li>2</li> + <li> + <a hef="#google">google</a> + </li> + </ul> + """ + + const info = xml""" + <item> + <i>1</i> + <i>2</i> + </item> + """ + + const shader = glsl""" + void main() + { + gl_Position = gl_ProjectionMatrix + * gl_ModelViewMatrix + * gl_Vertex; + } + """ + + + echo script + echo styles + echo body + echo info + echo shader diff --git a/tests/template/i2416.nim b/tests/template/i2416.nim new file mode 100644 index 000000000..4b53cd0ca --- /dev/null +++ b/tests/template/i2416.nim @@ -0,0 +1 @@ +template i2416*() = echo "i2416" diff --git a/tests/template/m1027a.nim b/tests/template/m1027a.nim new file mode 100644 index 000000000..fad915a2f --- /dev/null +++ b/tests/template/m1027a.nim @@ -0,0 +1 @@ +const version_str* = "mod a" diff --git a/tests/template/m1027b.nim b/tests/template/m1027b.nim new file mode 100644 index 000000000..5ff0b9714 --- /dev/null +++ b/tests/template/m1027b.nim @@ -0,0 +1 @@ +const version_str* = "mod b" diff --git a/tests/template/m19277_1.nim b/tests/template/m19277_1.nim new file mode 100644 index 000000000..840bd4767 --- /dev/null +++ b/tests/template/m19277_1.nim @@ -0,0 +1,2 @@ +template foo*(x: untyped) = + echo "got: ", x diff --git a/tests/template/m19277_2.nim b/tests/template/m19277_2.nim new file mode 100644 index 000000000..de72dad45 --- /dev/null +++ b/tests/template/m19277_2.nim @@ -0,0 +1,2 @@ +proc foo*(a: string) = + echo "got string: ", a diff --git a/tests/template/mcan_access_hidden_field.nim b/tests/template/mcan_access_hidden_field.nim new file mode 100644 index 000000000..2c0026ec1 --- /dev/null +++ b/tests/template/mcan_access_hidden_field.nim @@ -0,0 +1,8 @@ + +type + Foo* = object + fooa, foob: int + +proc createFoo*(a, b: int): Foo = Foo(fooa: a, foob: b) + +template geta*(f: Foo): untyped = f.fooa diff --git a/tests/template/mdotcall.nim b/tests/template/mdotcall.nim new file mode 100644 index 000000000..fecd8ee26 --- /dev/null +++ b/tests/template/mdotcall.nim @@ -0,0 +1,82 @@ +# issue #20073 + +type Foo = object +proc foo(f: Foo) = discard + +template works*() = + var f: Foo + foo(f) + +template boom*() = + var f: Foo + f.foo() # Error: attempting to call undeclared routine: 'foo' + f.foo # Error: undeclared field: 'foo' for type a.Foo + +# issue #7085 + +proc bar(a: string): string = + return a & "bar" + +template baz*(a: string): string = + var b = a.bar() + b + +# issue #7223 + +import mdotcall2 + +type + Bytes* = seq[byte] + + BytesRange* = object + bytes*: Bytes + ibegin*, iend*: int + +proc privateProc(r: BytesRange): int = r.ibegin + +template rangeBeginAddr*(r: BytesRange): pointer = + r.bytes.baseAddr.shift(r.privateProc) + +# issue #11733 + +type ObjA* = object + +proc foo2(o: var ObjA) = discard +proc bar2(o: var ObjA, arg: Natural) = discard + +template publicTemplateObjSyntax*(o: var ObjA, arg: Natural, doStuff: untyped) = + o.foo2() + doStuff + o.bar2(arg) + +# issue #15246 +import os + +template sourceBaseName*(): string = + bind splitFile + instantiationInfo().filename.splitFile().name + +# issue #12683 + +import unicode +template toRune(s: string): Rune = s.runeAt(0) +proc heh*[T](x: Slice[T], chars: string) = discard chars.toRune + +# issue #7889 + +from streams import newStringStream, readData, writeData + +template bindmeTemplate*(): untyped = + var tst = "sometext" + var ss = newStringStream("anothertext") + ss.writeData(tst[0].addr, 2) + discard ss.readData(tst[0].addr, 2) # <= comment this out to make compilation successful + +from macros import quote, newIdentNode + +macro bindmeQuote*(): untyped = + quote do: + var tst = "sometext" + var ss = newStringStream("anothertext") + ss.writeData(tst[0].addr, 2) + discard ss.readData(tst[0].addr, 2) # <= comment this out to make compilation successful diff --git a/tests/template/mdotcall2.nim b/tests/template/mdotcall2.nim new file mode 100644 index 000000000..e906ac9d6 --- /dev/null +++ b/tests/template/mdotcall2.nim @@ -0,0 +1,7 @@ +# imported by mdotcall + +proc baseAddr*[T](x: openarray[T]): pointer = + cast[pointer](x) + +proc shift*(p: pointer, delta: int): pointer = + cast[pointer](cast[int](p) + delta) diff --git a/tests/template/mgensym_generic_cross_module.nim b/tests/template/mgensym_generic_cross_module.nim new file mode 100644 index 000000000..ea88f67e6 --- /dev/null +++ b/tests/template/mgensym_generic_cross_module.nim @@ -0,0 +1,14 @@ + +template makeDomElement(x: untyped, name: string = "") = + const tag {.gensym.} = if name.len == 0: astToStr(x) else: name + + proc x*(p: int|float) = + echo tag, p + + proc x*(p: string|cstring) = + echo tag, p + +#proc wrappedUp[T](x: T) = +# mixin foo, bar +makeDomElement(foo, "foo") +makeDomElement(bar) diff --git a/tests/template/mlt.nim b/tests/template/mlt.nim new file mode 100644 index 000000000..e567cf085 --- /dev/null +++ b/tests/template/mlt.nim @@ -0,0 +1,3 @@ + +type Point* = ref object of RootObj +proc `>`*(p1, p2: Point): bool = false diff --git a/tests/template/mqualifiedtype1.nim b/tests/template/mqualifiedtype1.nim new file mode 100644 index 000000000..46569107f --- /dev/null +++ b/tests/template/mqualifiedtype1.nim @@ -0,0 +1,2 @@ +type A* = object + x*: int diff --git a/tests/template/mqualifiedtype2.nim b/tests/template/mqualifiedtype2.nim new file mode 100644 index 000000000..6a61c14bd --- /dev/null +++ b/tests/template/mqualifiedtype2.nim @@ -0,0 +1,2 @@ +type A* = object + x*: array[1000, byte] diff --git a/tests/template/mtempl5.nim b/tests/template/mtempl5.nim new file mode 100644 index 000000000..2cc6f91bc --- /dev/null +++ b/tests/template/mtempl5.nim @@ -0,0 +1,24 @@ + +var + gx = 88 + gy = 44 + +template templ*(): int = + bind gx, gy + gx + gy + +import json + +const + codeField = "foobar" + messageField = "more" + +template trap*(path: string, body: untyped): untyped = + #bind codeField, messageField + try: + body + except: + let msg = getCurrentExceptionMsg() + #debug "Error occurred within RPC ", path = path, errorMessage = msg + result = %*{codeField: "SERVER_ERROR", messageField: msg} + diff --git a/tests/template/otests.nim b/tests/template/otests.nim new file mode 100644 index 000000000..9cb9d7fcb --- /dev/null +++ b/tests/template/otests.nim @@ -0,0 +1,219 @@ +# Fields +const x = 5 + + +# Test substring +static: + assert "test".substring(3) == "t" + assert "test".substring(2,1) == "s" + assert "test".substring(3,2) == "t" + assert "test".substring(1,2) == "es" + + +# Various parsing tests +when true: + + block: #no_substitution + proc actual: string = tmpli html""" + <p>Test!</p> + """ + const expected = html""" + <p>Test!</p> + """ + doAssert actual() == expected + + block: #basic + proc actual: string = tmpli html""" + <p>Test $$x</p> + $x + """ + const expected = html""" + <p>Test $x</p> + 5 + """ + doAssert actual() == expected + + block: #expression + proc actual: string = tmpli html""" + <p>Test $$(x * 5)</p> + $(x * 5) + """ + const expected = html""" + <p>Test $(x * 5)</p> + 25 + """ + doAssert actual() == expected + + block: #escape + proc actual: string = tmpli js""" + [{ + "hello world" + }] + """ + const expected = js""" + [{ + "hello world" + }] + """ + doAssert actual() == expected + + block: #forIn + proc actual: string = tmpli html""" + <p>Test for</p> + <ul> + $for y in 0..2 { + <li>$y</li> + } + </ul> + """ + const expected = html""" + <p>Test for</p> + <ul> + <li>0</li> + <li>1</li> + <li>2</li> + </ul> + """ + doAssert actual() == expected + + block: #while + proc actual: string = tmpli html""" + <p>Test while/stmt</p> + <ul> + ${ var y = 0 } + $while y < 4 { + <li>$y</li> + ${ inc(y) } + } + </ul> + """ + const expected = html""" + <p>Test while/stmt</p> + <ul> + <li>0</li> + <li>1</li> + <li>2</li> + <li>3</li> + </ul> + """ + doAssert actual() == expected + + block: #ifElifElse + proc actual: string = tmpli html""" + <p>Test if/elif/else</p> + $if x == 8 { + <div>x is 8!</div> + } + $elif x == 7 { + <div>x is 7!</div> + } + $else { + <div>x is neither!</div> + } + """ + const expected = html""" + <p>Test if/elif/else</p> + <div>x is neither!</div> + """ + doAssert actual() == expected + + block: #multiLineStatements + proc actual: string = tmpli html""" + <p>Test multiline statements</p> + ${ + var x = 5 + var y = 7 + } + <span>$x</span><span>$y</span> + """ + const expected = html""" + <p>Test multiline statements</p> + <span>5</span><span>7</span> + """ + doAssert actual() == expected + + block: #caseOfElse + proc actual: string = tmpli html""" + <p>Test case</p> + $case x + $of 5 { + <div>x == 5</div> + } + $of 6 { + <div>x == 6</div> + } + $else {} + """ + const expected = html""" + <p>Test case</p> + <div>x == 5</div> + """ + doAssert actual() == expected + + + +when true: #embeddingTest + proc no_substitution: string = tmpli html""" + <h1>Template test!</h1> + """ + + # # Single variable substitution + proc substitution(who = "nobody"): string = tmpli html""" + <div id="greeting">hello $who!</div> + """ + + # Expression template + proc test_expression(nums: openArray[int] = []): string = + var i = 2 + tmpli html""" + $(no_substitution()) + $(substitution("Billy")) + <div id="age">Age: $($nums[i] & "!!")</div> + """ + + proc test_statements(nums: openArray[int] = []): string = + tmpli html""" + $(test_expression(nums)) + $if true { + <ul> + $for i in nums { + <li>$(i * 2)</li> + } + </ul> + } + """ + + var actual = test_statements([0,1,2]) + const expected = html""" + <h1>Template test!</h1> + <div id="greeting">hello Billy!</div> + <div id="age">Age: 2!!</div> + <ul> + <li>0</li> + <li>2</li> + <li>4</li> + </ul> + """ + doAssert actual == expected + + +when defined(future): + block: #tryCatch + proc actual: string = tmpli html""" + <p>Test try/catch</p> + <div> + $try { + <div>Lets try this!</div> + } + $except { + <div>Uh oh!</div> + } + </div> + """ + const expected = html""" + <p>Test try/catch</p> + <div> + <div>Lets try this!</div> + </div> + """ + doAssert actual() == expected diff --git a/tests/template/sunset.nimf b/tests/template/sunset.nimf new file mode 100644 index 000000000..bd57bb8e1 --- /dev/null +++ b/tests/template/sunset.nimf @@ -0,0 +1,68 @@ +#? stdtmpl +#proc sunsetTemplate*(current, ticker, content: string, +# tabs: openArray[array[0..1, string]]): string = +# result = "" +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> + +<head> + <title>Nimrod Programming System</title> + <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> + <link rel="stylesheet" type="text/css" href="style/style.css" /> +</head> + +<body> + <div id="main"> + <div id="links"> + <!-- **** INSERT LINKS HERE **** --> + </div> + <div id="logo"><h1>Nimrod Programming System</h1></div> + <div id="content"> + <div id="menu"> + <ul> + #for item in items(tabs): + #var name = item[0] + #var t = item[1] + #if t == current: + <li><a id="selected" href="${t}.html" title = "Nimrod - $name">$name</a></li> + #else: + <li><a href="${t}.html" title = "Nimrod - $name">$name</a></li> + #end if + #end for + </ul> + </div> + <div id="column1"> + <div class="sidebaritem"> + <div class="sbihead"> + <h1>latest news</h1> + </div> + <div class="sbicontent"> + $ticker + </div> + </div> + <div class="sidebaritem"> + <div class="sbihead"> + <h1>additional links</h1> + </div> + <div class="sbilinks"> + <!-- **** INSERT ADDITIONAL LINKS HERE **** --> + <ul> + <li><a class="reference" href="http://llvm.org">LLVM</a></li> + <li><a class="reference" href="http://gcc.gnu.org">GCC</a></li> + </ul> + </div> + </div> + </div> + <div id="column2"> + $content + </div> + </div> + <div id="footer"> + copyright © 2008 Andreas Rumpf | Last update: ${getDateStr()} + | <a class="reference" href="http://validator.w3.org/check?uri=referer">XHTML 1.1</a> + | <a class="reference" href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> + | <a class="reference" href="http://www.dcarter.co.uk">design by dcarter</a> + </div> + </div> +</body> +</html> diff --git a/tests/template/t1027.nim b/tests/template/t1027.nim new file mode 100644 index 000000000..fe03c4ea9 --- /dev/null +++ b/tests/template/t1027.nim @@ -0,0 +1,22 @@ +discard """ + cmd: "nim check --hints:off $file" + errormsg: "" + nimout: ''' +t1027.nim(20, 19) Error: ambiguous identifier: 'version_str' -- use one of the following: + m1027a.version_str: string + m1027b.version_str: string +''' +""" + + + + + + +import m1027a, m1027b + +# bug #1027 +template wrap_me(stuff): untyped = + echo "Using " & version_str + +wrap_me("hey") diff --git a/tests/template/t11705.nim b/tests/template/t11705.nim new file mode 100644 index 000000000..65ddc7e6c --- /dev/null +++ b/tests/template/t11705.nim @@ -0,0 +1,17 @@ +type RefObj = ref object + +proc `[]`(val: static[int]) = # works with different name/overload or without static arg + discard + +template noRef*(T: typedesc): typedesc = # works without template indirection + typeof(default(T)[]) + +proc `=destroy`(x: var noRef(RefObj)) = + discard + +proc foo = + var x = new RefObj + doAssert $(x[]) == "()" + +# bug #11705 +foo() diff --git a/tests/template/t13426.nim b/tests/template/t13426.nim new file mode 100644 index 000000000..f7f44749c --- /dev/null +++ b/tests/template/t13426.nim @@ -0,0 +1,87 @@ +discard """ + cmd: "nim check --hints:off $file" + errormsg: "" + nimout: ''' +t13426.nim(81, 6) template/generic instantiation of `fun` from here +t13426.nim(80, 24) Error: type mismatch: got <int> but expected 'string' +t13426.nim(81, 6) template/generic instantiation of `fun` from here +t13426.nim(80, 17) Error: type mismatch: got <uint, string> +but expected one of: +proc `and`(x, y: uint): uint + first type mismatch at position: 2 + required type for y: uint + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint64): uint64 + first type mismatch at position: 2 + required type for y: uint64 + but expression 'high(@[1])' is of type: string +10 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them + +expression: 1'u and high(@[1]) +t13426.nim(81, 6) template/generic instantiation of `fun` from here +t13426.nim(80, 17) Error: expression '' has no type (or is ambiguous) +t13426.nim(87, 6) template/generic instantiation of `fun` from here +t13426.nim(86, 22) Error: type mismatch: got <int> but expected 'string' +t13426.nim(87, 6) template/generic instantiation of `fun` from here +t13426.nim(86, 15) Error: type mismatch: got <int literal(1), string> +but expected one of: +proc `and`(x, y: int): int + first type mismatch at position: 2 + required type for y: int + but expression 'high(@[1])' is of type: string +proc `and`(x, y: int16): int16 + first type mismatch at position: 2 + required type for y: int16 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: int32): int32 + first type mismatch at position: 2 + required type for y: int32 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: int64): int64 + first type mismatch at position: 2 + required type for y: int64 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: int8): int8 + first type mismatch at position: 2 + required type for y: int8 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint): uint + first type mismatch at position: 2 + required type for y: uint + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint16): uint16 + first type mismatch at position: 2 + required type for y: uint16 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint32): uint32 + first type mismatch at position: 2 + required type for y: uint32 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint64): uint64 + first type mismatch at position: 2 + required type for y: uint64 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint8): uint8 + first type mismatch at position: 2 + required type for y: uint8 + but expression 'high(@[1])' is of type: string +2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them + +expression: 1 and high(@[1]) +t13426.nim(87, 6) template/generic instantiation of `fun` from here +t13426.nim(86, 15) Error: expression '' has no type (or is ambiguous) +''' +""" + +# bug # #13426 +block: + template bar(t): string = high(t) + proc fun[A](key: A) = + var h = 1'u and bar(@[1]) + fun(0) + +block: + template bar(t): string = high(t) + proc fun[A](key: A) = + var h = 1 and bar(@[1]) + fun(0) diff --git a/tests/template/t17433.nim b/tests/template/t17433.nim new file mode 100644 index 000000000..053d33ff0 --- /dev/null +++ b/tests/template/t17433.nim @@ -0,0 +1,16 @@ +# Inside template bodies, ensure return types referencing a param are replaced. +# This helps guarantee that return parameter analysis happens after argument +# analysis. + +# bug #17433 + +from std/macros import expandMacros + +proc bar(a: typedesc): a = default(a) +doAssert bar(float) == 0.0 +doAssert bar(string) == "" + +template main = + proc baz(a: typedesc): a = default(a) + doAssert baz(float) == 0.0 +main() diff --git a/tests/template/t18113.nim b/tests/template/t18113.nim new file mode 100644 index 000000000..a84b3fd0e --- /dev/null +++ b/tests/template/t18113.nim @@ -0,0 +1,14 @@ +# ensure template pragma handling doesn't eagerly attempt to add an implicit +# 'pushed' pragma to the evaluation of any intermediate AST prior to +# substitution. + +# bug #18113 + +import sequtils + +{.push raises: [Defect].} + +var a = toSeq([1, 2, 3, 5, 10]).filterIt(it > 5) + +doAssert a.len == 1 +doAssert a[0] == 10 diff --git a/tests/template/t19149.nim b/tests/template/t19149.nim new file mode 100644 index 000000000..631e8fc30 --- /dev/null +++ b/tests/template/t19149.nim @@ -0,0 +1,19 @@ +type Foo = tuple[active: bool, index: int] + + +var f: Foo + +# template result type during match stage +# f:var Foo +# a:Foo +# tyVar +# tyTuple +# after change to proc +# f:Foo +# a:Foo +# tyTuple +# tyTuple + +template cursor(): var Foo = f +discard cursor() + diff --git a/tests/template/t19277.nim b/tests/template/t19277.nim new file mode 100644 index 000000000..16435a09c --- /dev/null +++ b/tests/template/t19277.nim @@ -0,0 +1,19 @@ +discard """ + output: ''' +got: 0 +''' +""" + +# issue #19277 + +import m19277_1, m19277_2 + +template injector(val: untyped): untyped = + template subtemplate: untyped = val + subtemplate() + +template methodCall(val: untyped): untyped = val + +{.push raises: [Defect].} + +foo(injector(0).methodCall()) diff --git a/tests/template/t19700.nim b/tests/template/t19700.nim new file mode 100644 index 000000000..cc2944944 --- /dev/null +++ b/tests/template/t19700.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "type mismatch: got <Obj, Obj, template (x: untyped, y: untyped): untyped>" +""" + +type Obj = object + +proc apply[T, R](a, b: T; f: proc(x, y: T): R): R = f(a, b) + +let a, b = Obj() +discard apply(a, b, `!=`) diff --git a/tests/template/t21231.nim b/tests/template/t21231.nim new file mode 100644 index 000000000..51e96cdd6 --- /dev/null +++ b/tests/template/t21231.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "undeclared identifier: 'x'" +""" + +var x: int +# bug #21231 +template f(y: untyped) = echo y.x +for i in 1 .. 10: + x = i + f(system) diff --git a/tests/template/t21532.nim b/tests/template/t21532.nim new file mode 100644 index 000000000..3193b0dc3 --- /dev/null +++ b/tests/template/t21532.nim @@ -0,0 +1,8 @@ + +template elementType(a: untyped): typedesc = + typeof(block: (for ai in a: ai)) + +func fn[T](a: T) = + doAssert elementType(a) is int + +@[1,2,3].fn \ No newline at end of file diff --git a/tests/template/t24112.nim b/tests/template/t24112.nim new file mode 100644 index 000000000..175fc7d5e --- /dev/null +++ b/tests/template/t24112.nim @@ -0,0 +1,19 @@ +discard """ + matrix: "--skipParentCfg --filenames:legacyRelProj --hints:off" + action: reject +""" + +# issue #24112, needs --experimental:openSym disabled + +block: # simplified + type + SomeObj = ref object # Doesn't error if you make SomeObj be non-ref + template foo = yield SomeObj() + when compiles(foo): discard + +import std/asyncdispatch +block: + proc someProc(): Future[void] {.async.} = discard + proc foo() = + await someProc() #[tt.Error + ^ Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead]# diff --git a/tests/template/t6217.nim b/tests/template/t6217.nim new file mode 100644 index 000000000..b27b61881 --- /dev/null +++ b/tests/template/t6217.nim @@ -0,0 +1,19 @@ +discard """ + output: ''' +start +side effect! +end +''' +""" + +# bug #6217 + +template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a + +proc f(): int = + echo "side effect!" + result = 55 + +echo "start" +doAssert f() * 2 == 110 +echo "end" diff --git a/tests/template/t9534.nim b/tests/template/t9534.nim new file mode 100644 index 000000000..8d66f42a0 --- /dev/null +++ b/tests/template/t9534.nim @@ -0,0 +1,21 @@ +discard """ + targets: "c cpp" +""" + +# bug #9534 +type + Object = object + data: int + +template test() = + proc methodName(o: Object): int = + var p: pointer + doAssert o.data == 521 + let f {.used.} = cast[proc (o: int): int {.nimcall.}](p) + doAssert o.data == 521 + result = 1314 + + var a = Object(data: 521) + doAssert methodName(a) == 1314 + +test() diff --git a/tests/template/t_otemplates.nim b/tests/template/t_otemplates.nim new file mode 100644 index 000000000..b278bea0f --- /dev/null +++ b/tests/template/t_otemplates.nim @@ -0,0 +1,345 @@ +discard """ + output: "Success" +""" + +# Ref: +# http://nim-lang.org/macros.html +# http://nim-lang.org/parseutils.html + + +# Imports +import tables, parseutils, macros, strutils +import annotate +export annotate + + +# Fields +const identChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} + + +# Procedure Declarations +proc parse_template(node: NimNode, value: string) {.compiletime.} + + +# Procedure Definitions +proc substring(value: string, index: int, length = -1): string {.compiletime.} = + ## Returns a string at most `length` characters long, starting at `index`. + return if length < 0: value.substr(index) + elif length == 0: "" + else: value.substr(index, index + length-1) + + +proc parse_thru_eol(value: string, index: int): int {.compiletime.} = + ## Reads until and past the end of the current line, unless + ## a non-whitespace character is encountered first + var remainder: string + var read = value.parseUntil(remainder, {0x0A.char}, index) + if remainder.skipWhitespace() == read: + return read + 1 + + +proc trim_after_eol(value: var string) {.compiletime.} = + ## Trims any whitespace at end after \n + var toTrim = 0 + for i in countdown(value.len-1, 0): + # If \n, return + if value[i] in [' ', '\t']: inc(toTrim) + else: break + + if toTrim > 0: + value = value.substring(0, value.len - toTrim) + + +proc trim_eol(value: var string) {.compiletime.} = + ## Removes everything after the last line if it contains nothing but whitespace + for i in countdown(value.len - 1, 0): + # If \n, trim and return + if value[i] == 0x0A.char: + value = value.substr(0, i) + break + + # This is the first character + if i == 0: + value = "" + break + + # Skip change + if not (value[i] in [' ', '\t']): break + + +proc detect_indent(value: string, index: int): int {.compiletime.} = + ## Detects how indented the line at `index` is. + # Seek to the beginning of the line. + var lastChar = index + for i in countdown(index, 0): + if value[i] == 0x0A.char: + # if \n, return the indentation level + return lastChar - i + elif not (value[i] in [' ', '\t']): + # if non-whitespace char, decrement lastChar + dec(lastChar) + + +proc parse_thru_string(value: string, i: var int, strType = '"') {.compiletime.} = + ## Parses until ending " or ' is reached. + inc(i) + if i < value.len-1: + inc(i, value.skipUntil({'\\', strType}, i)) + + +proc parse_to_close(value: string, index: int, open='(', close=')', opened=0): int {.compiletime.} = + ## Reads until all opened braces are closed + ## ignoring any strings "" or '' + var remainder = value.substring(index) + var open_braces = opened + result = 0 + + while result < remainder.len: + var c = remainder[result] + + if c == open: inc(open_braces) + elif c == close: dec(open_braces) + elif c == '"': remainder.parse_thru_string(result) + elif c == '\'': remainder.parse_thru_string(result, '\'') + + if open_braces == 0: break + else: inc(result) + + +iterator parse_stmt_list(value: string, index: var int): string = + ## Parses unguided ${..} block + var read = value.parse_to_close(index, open='{', close='}') + var expressions = value.substring(index + 1, read - 1).split({ ';', 0x0A.char }) + + for expression in expressions: + let value = expression.strip + if value.len > 0: + yield value + + #Increment index & parse thru EOL + inc(index, read + 1) + inc(index, value.parse_thru_eol(index)) + + +iterator parse_compound_statements(value, identifier: string, index: int): string = + ## Parses through several statements, i.e. if {} elif {} else {} + ## and returns the initialization of each as an empty statement + ## i.e. if x == 5 { ... } becomes if x == 5: nil. + + template get_next_ident(expected) = + var nextIdent: string + discard value.parseWhile(nextIdent, {'$'} + identChars, i) + + var next: string + var read: int + + if nextIdent == "case": + # We have to handle case a bit differently + read = value.parseUntil(next, '$', i) + inc(i, read) + yield next.strip(leading=false) & "\n" + + else: + read = value.parseUntil(next, '{', i) + + if nextIdent in expected: + inc(i, read) + # Parse until closing }, then skip whitespace afterwards + read = value.parse_to_close(i, open='{', close='}') + inc(i, read + 1) + inc(i, value.skipWhitespace(i)) + yield next & ": nil\n" + + else: break + + + var i = index + while true: + # Check if next statement would be valid, given the identifier + if identifier in ["if", "when"]: + get_next_ident([identifier, "$elif", "$else"]) + + elif identifier == "case": + get_next_ident(["case", "$of", "$elif", "$else"]) + + elif identifier == "try": + get_next_ident(["try", "$except", "$finally"]) + + +proc parse_complex_stmt(value, identifier: string, index: var int): NimNode {.compiletime.} = + ## Parses if/when/try /elif /else /except /finally statements + + # Build up complex statement string + var stmtString = newString(0) + var numStatements = 0 + for statement in value.parse_compound_statements(identifier, index): + if statement[0] == '$': stmtString.add(statement.substr(1)) + else: stmtString.add(statement) + inc(numStatements) + + # Parse stmt string + result = parseExpr(stmtString) + + var resultIndex = 0 + + # Fast forward a bit if this is a case statement + if identifier == "case": + inc(resultIndex) + + while resultIndex < numStatements: + + # Detect indentation + let indent = detect_indent(value, index) + + # Parse until an open brace `{` + var read = value.skipUntil('{', index) + inc(index, read + 1) + + # Parse through EOL + inc(index, value.parse_thru_eol(index)) + + # Parse through { .. } + read = value.parse_to_close(index, open='{', close='}', opened=1) + + # Add parsed sub-expression into body + var body = newStmtList() + var stmtString = value.substring(index, read) + trim_after_eol(stmtString) + stmtString = reindent(stmtString, indent) + parse_template(body, stmtString) + inc(index, read + 1) + + # Insert body into result + var stmtIndex = result[resultIndex].len-1 + result[resultIndex][stmtIndex] = body + + # Parse through EOL again & increment result index + inc(index, value.parse_thru_eol(index)) + inc(resultIndex) + + +proc parse_simple_statement(value: string, index: var int): NimNode {.compiletime.} = + ## Parses for/while + + # Detect indentation + let indent = detect_indent(value, index) + + # Parse until an open brace `{` + var splitValue: string + var read = value.parseUntil(splitValue, '{', index) + result = parseExpr(splitValue & ":nil") + inc(index, read + 1) + + # Parse through EOL + inc(index, value.parse_thru_eol(index)) + + # Parse through { .. } + read = value.parse_to_close(index, open='{', close='}', opened=1) + + # Add parsed sub-expression into body + var body = newStmtList() + var stmtString = value.substring(index, read) + trim_after_eol(stmtString) + stmtString = reindent(stmtString, indent) + parse_template(body, stmtString) + inc(index, read + 1) + + # Insert body into result + var stmtIndex = result.len-1 + result[stmtIndex] = body + + # Parse through EOL again + inc(index, value.parse_thru_eol(index)) + + +proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.compiletime.} = + ## Parses a string until a $ symbol is encountered, if + ## two $$'s are encountered in a row, a split will happen + ## removing one of the $'s from the resulting output + var splitValue: string + var read = value.parseUntil(splitValue, '$', index) + var insertionPoint = node.len + + inc(index, read + 1) + if index < value.len: + + case value[index] + of '$': + # Check for duplicate `$`, meaning this is an escaped $ + node.add newCall("add", ident("result"), newStrLitNode("$")) + inc(index) + + of '(': + # Check for open `(`, which means parse as simple single-line expression. + trim_eol(splitValue) + read = value.parse_to_close(index) + 1 + node.add newCall("add", ident("result"), + newCall(bindSym"strip", parseExpr("$" & value.substring(index, read))) + ) + inc(index, read) + + of '{': + # Check for open `{`, which means open statement list + trim_eol(splitValue) + for s in value.parse_stmt_list(index): + node.add parseExpr(s) + + else: + # Otherwise parse while valid `identChars` and make expression w/ $ + var identifier: string + read = value.parseWhile(identifier, identChars, index) + + if identifier in ["for", "while"]: + ## for/while means open simple statement + trim_eol(splitValue) + node.add value.parse_simple_statement(index) + + elif identifier in ["if", "when", "case", "try"]: + ## if/when/case/try means complex statement + trim_eol(splitValue) + node.add value.parse_complex_stmt(identifier, index) + + elif identifier.len > 0: + ## Treat as simple variable + node.add newCall("add", ident("result"), newCall("$", ident(identifier))) + inc(index, read) + + result = true + + # Insert + if splitValue.len > 0: + node.insert insertionPoint, newCall("add", ident("result"), newStrLitNode(splitValue)) + + +proc parse_template(node: NimNode, value: string) = + ## Parses through entire template, outputting valid + ## Nim code into the input `node` AST. + var index = 0 + while index < value.len and + parse_until_symbol(node, value, index): discard + + +macro tmpli*(body: untyped): untyped = + result = newStmtList() + + result.add parseExpr("result = \"\"") + + var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal + else: body[1].strVal + + parse_template(result, reindent(value)) + + +macro tmpl*(body: untyped): untyped = + result = newStmtList() + + var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal + else: body[1].strVal + + parse_template(result, reindent(value)) + + +# Run tests +when true: + include otests + echo "Success" diff --git a/tests/template/taliassyntax.nim b/tests/template/taliassyntax.nim new file mode 100644 index 000000000..2d8237d93 --- /dev/null +++ b/tests/template/taliassyntax.nim @@ -0,0 +1,74 @@ +type Foo = object + bar: int + +var foo = Foo(bar: 10) +template bar: int = foo.bar +doAssert bar == 10 +bar = 15 +doAssert bar == 15 +var foo2 = Foo(bar: -10) +doAssert bar == 15 +# works in generics +proc genericProc[T](x: T): string = + $(x, bar) +doAssert genericProc(true) == "(true, 15)" +# redefine +template bar: int {.redefine.} = foo2.bar +doAssert bar == -10 + +block: # subscript + var bazVal = @[1, 2, 3] + template baz: seq[int] = bazVal + doAssert baz[1] == 2 + proc genericProc2[T](x: T): string = + result = $(x, baz[1]) + baz[1] = 7 + doAssert genericProc2(true) == "(true, 2)" + doAssert baz[1] == 7 + baz[1] = 14 + doAssert baz[1] == 14 + +block: # type alias + template Int2: untyped = int + let x: Int2 = 123 + proc generic[T](): string = + template U: untyped = T + var x: U + result = $typeof(x) + doAssert result == $U + doAssert result == $T + doAssert generic[int]() == "int" + doAssert generic[Int2]() == "int" + doAssert generic[string]() == "string" + doAssert generic[seq[int]]() == "seq[int]" + doAssert generic[seq[Int2]]() == "seq[int]" + discard generic[123]() + proc genericStatic[X; T: static[X]](): string = + template U: untyped = T + result = $U + doAssert result == $T + doAssert genericStatic[int, 123]() == "123" + doAssert genericStatic[Int2, 123]() == "123" + doAssert genericStatic[(string, bool), ("a", true)]() == "(\"a\", true)" + +block: # issue #13515 + template test: bool = true + # compiles: + if not test: + doAssert false + # does not compile: + template x = + if not test: + doAssert false + x + +import macros + +block: # issue #21727 + template debugAnnotation(s: typed): string = + astToStr s + + macro cpsJump(x: int): untyped = + result = newLit(debugAnnotation(cpsJump)) + + doAssert cpsJump(13) == "cpsJump" diff --git a/tests/template/taliassyntaxerrors.nim b/tests/template/taliassyntaxerrors.nim new file mode 100644 index 000000000..f16acaf91 --- /dev/null +++ b/tests/template/taliassyntaxerrors.nim @@ -0,0 +1,28 @@ +discard """ + cmd: "nim check --hints:off $file" +""" + +block: # with params + type Foo = object + bar: int + + var foo = Foo(bar: 10) + template bar(x: int): int = x + foo.bar + let a = bar #[tt.Error + ^ invalid type: 'template (x: int): int' for let. Did you mean to call the template with '()'?]# + bar = 15 #[tt.Error + ^ 'bar' cannot be assigned to]# + +block: # generic template + type Foo = object + bar: int + + var foo = Foo(bar: 10) + template bar[T]: T = T(foo.bar) + let a = bar #[tt.Error + ^ invalid type: 'template (): T' for let. Did you mean to call the template with '()'?; tt.Error + ^ 'bar' has unspecified generic parameters]# + let b = bar[float]() + doAssert b == 10.0 + bar = 15 #[tt.Error + ^ 'bar' cannot be assigned to]# diff --git a/tests/template/tcallsitelineinfo.nim b/tests/template/tcallsitelineinfo.nim new file mode 100644 index 000000000..5fed93363 --- /dev/null +++ b/tests/template/tcallsitelineinfo.nim @@ -0,0 +1,17 @@ +discard """ + nimout: ''' +tcallsitelineinfo.nim(17, 4) Warning: abc [User] +''' + exitcode: 1 + outputsub: ''' +tcallsitelineinfo.nim(17) tcallsitelineinfo +Error: unhandled exception: def [ValueError] +''' +""" + +template foo(): untyped {.callsite.} = + {.warning: "abc".} + raise newException(ValueError, "def") + echo "hello" + +foo() diff --git a/tests/template/tcallsitelineinfo2.nim b/tests/template/tcallsitelineinfo2.nim new file mode 100644 index 000000000..d5f257474 --- /dev/null +++ b/tests/template/tcallsitelineinfo2.nim @@ -0,0 +1,20 @@ +discard """ + nimout: ''' +tcallsitelineinfo2.nim(18, 1) Warning: abc [User] +tcallsitelineinfo2.nim(19, 12) Warning: def [User] +''' + exitcode: 1 + outputsub: ''' +tcallsitelineinfo2.nim(20) tcallsitelineinfo2 +Error: unhandled exception: ghi [ValueError] +''' +""" + +template foo(a: untyped): untyped {.callsite.} = + {.warning: "abc".} + a + echo "hello" + +foo: # with `{.line.}:`, the following do not keep their line information: + {.warning: "def".} + raise newException(ValueError, "ghi") diff --git a/tests/template/tconfusinglocal.nim b/tests/template/tconfusinglocal.nim new file mode 100644 index 000000000..9f641e2bf --- /dev/null +++ b/tests/template/tconfusinglocal.nim @@ -0,0 +1,21 @@ +discard """ +output: "0" +""" + + +# bug #5135 +proc fail*[E](e: E): void = + raise newException(Exception, e) + +# bug #4875 +type Bar = object + mFoo: int + +template foo(a: Bar): int = a.mFoo + +proc main = + let foo = 5 # Rename this to smth else to make it work + var b: Bar + echo b.foo + +main() diff --git a/tests/template/tdefaultparam.nim b/tests/template/tdefaultparam.nim new file mode 100644 index 000000000..7ea0b2b25 --- /dev/null +++ b/tests/template/tdefaultparam.nim @@ -0,0 +1,56 @@ +block: + template foo(a: untyped, b: untyped = a(0)): untyped = + let x = a(0) + let y = b + (x, y) + proc bar(x: int): int = x + 1 + doAssert foo(bar, b = bar(0)) == (1, 1) + doAssert foo(bar) == (1, 1) + +block: # issue #23506 + var a: string + template foo(x: int; y = x) = + a = $($x, $y) + foo(1) + doAssert a == "(\"1\", \"1\")" + +block: # untyped params with default value + macro foo(x: typed): untyped = + result = x + template test(body: untyped, alt: untyped = (;), maxTries = 3): untyped {.foo.} = + body + alt + var s = "a" + test: + s.add "b" + do: + s.add "c" + doAssert s == "abc" + template test2(body: untyped, alt: untyped = s.add("e"), maxTries = 3): untyped = + body + alt + test2: + s.add "d" + doAssert s == "abcde" + template test3(body: untyped = willNotCompile) = + discard + test3() + +block: # typed params with `void` default value + macro foo(x: typed): untyped = + result = x + template test(body: untyped, alt: typed = (;), maxTries = 3): untyped {.foo.} = + body + alt + var s = "a" + test: + s.add "b" + do: + s.add "c" + doAssert s == "abc" + template test2(body: untyped, alt: typed = s.add("e"), maxTries = 3): untyped = + body + alt + test2: + s.add "d" + doAssert s == "abcde" diff --git a/tests/template/tdefined_overload.nim b/tests/template/tdefined_overload.nim new file mode 100644 index 000000000..bcc83e414 --- /dev/null +++ b/tests/template/tdefined_overload.nim @@ -0,0 +1,46 @@ +discard """ + output: "Valid and not defined" +""" +# test for issue #7997 +# checking for `when not defined` in a template for some compile time symbol +# results in a compilation error of: +# Error: obsolete usage of 'defined', use 'declared' instead +# if the symbol is 'overloaded' by some variable or procedure, because in +# that case the argument of `defined` is of kind `nkSym` instead of `nkIdent` +# (for which was checked in `semexprs.semDefined`). + +block: + # check whether a proc with the same name as the argument to `defined` + # compiles + proc overloaded() = + discard + + template definedCheck(): untyped = + when not defined(overloaded): true + else: false + doAssert definedCheck == true + +block: + # check whether a variable with the same name as the argument to `defined` + # compiles + var overloaded: int + + template definedCheck(): untyped = + when not defined(overloaded): true + else: false + doAssert definedCheck == true + +block: + # check whether a non overloaded when check still works properly + when not defined(validIdentifier): + echo "Valid and not defined" + +block: + # now check that invalid identifiers cause a compilation error + # by using reject template. + template reject(b) = + static: doAssert(not compiles(b)) + + reject: + when defined(123): + echo "Invalid identifier! Will not be echoed" diff --git a/tests/template/tdotcall.nim b/tests/template/tdotcall.nim new file mode 100644 index 000000000..dc97fd52e --- /dev/null +++ b/tests/template/tdotcall.nim @@ -0,0 +1,32 @@ +import mdotcall + +block: # issue #20073 + works() + boom() + +block: # issue #7085 + doAssert baz("hello") == "hellobar" + doAssert baz"hello" == "hellobar" + doAssert "hello".baz == "hellobar" + +block: # issue #7223 + var r = BytesRange(bytes: @[1.byte, 2, 3], ibegin: 0, iend: 2) + var a = r.rangeBeginAddr + +block: # issue #11733 + var a: ObjA + var evaluated = false + a.publicTemplateObjSyntax(42): evaluated = true + doAssert evaluated + +block: # issue #15246 + doAssert sourceBaseName() == "tdotcall" + +block: # issue #12683 + heh(0..40, "|") + +block: # issue #7889 + if false: + bindmeQuote() + if false: + bindmeTemplate() diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim new file mode 100644 index 000000000..58c40941d --- /dev/null +++ b/tests/template/template_issues.nim @@ -0,0 +1,304 @@ +discard """ +output: ''' +@[] +5 +0 +a +hi +Hello, World! +(e: 42) +hey +foo +foo +foo +false +true +''' +""" + + +import macros, json + + +block t2057: + proc mpf_get_d(x: int): float = float(x) + proc mpf_cmp_d(a: int; b: float): int = 0 + + template toFloatHelper(result, tooSmall, tooLarge: untyped) = + result = mpf_get_d(a) + if result == 0.0 and mpf_cmp_d(a,0.0) != 0: + tooSmall + if result == Inf: + tooLarge + + proc toFloat(a: int): float = + toFloatHelper(result) do: + raise newException(ValueError, "number too small") + do: + raise newException(ValueError, "number too large") + + doAssert toFloat(8) == 8.0 + + + +import sequtils, os +block t2629: + template glob_rst(basedir: string = ""): untyped = + if baseDir.len == 0: + to_seq(walk_files("*.rst")) + else: + to_seq(walk_files(basedir/"*.rst")) + + let rst_files = concat(glob_rst(), glob_rst("docs")) + + when true: echo rst_files + + +block t5417: + macro genBody: untyped = + let sbx = genSym(nskLabel, "test") + when true: + result = quote do: + block `sbx`: + break `sbx` + else: + template foo(s1, s2) = + block s1: + break s2 + result = getAst foo(sbx, sbx) + + proc test() = + genBody() + + + +block t909: + template baz() = + proc bar() = + var x = 5 + iterator foo(): int {.closure.} = + echo x + var y = foo + discard y() + + macro test(): untyped = + result = getAst(baz()) + + test() + bar() + + + +block t993: + type PNode = ref object of RootObj + + template litNode(name, ty) = + type name = ref object of PNode + val: ty + litNode PIntNode, int + + template withKey(j: JsonNode; key: string; varname, + body: untyped): typed = + if j.hasKey(key): + let varname{.inject.}= j[key] + block: + body + + var j = parsejson("{\"zzz\":1}") + withkey(j, "foo", x): + echo(x) + + + + +block t1337: + template someIt(a, pred): untyped = + var it {.inject.} = 0 + pred + + proc aProc(n: auto) = + n.someIt(echo(it)) + + aProc(89) + + + +import mlt +block t4564: + type Bar = ref object of RootObj + proc foo(a: Bar): int = 0 + var a: Bar + let b = a.foo() > 0 + + + +block t8052: + type + UintImpl[N: static[int], T: SomeUnsignedInt] = object + raw_data: array[N, T] + + template genLoHi(TypeImpl: untyped): untyped = + template loImpl[N: static[int], T: SomeUnsignedInt](dst: TypeImpl[N div 2, T], src: TypeImpl[N, T]) = + let halfSize = N div 2 + for i in 0 ..< halfSize: + dst.raw_data[i] = src.raw_data[i] + + proc lo[N: static[int], T: SomeUnsignedInt](x: TypeImpl[N,T]): TypeImpl[N div 2, T] {.inline.}= + loImpl(result, x) + + genLoHi(UintImpl) + + var a: UintImpl[4, uint32] + + a.raw_data = [1'u32, 2'u32, 3'u32, 4'u32] + doAssert a.lo.raw_data.len == 2 + doAssert a.lo.raw_data[0] == 1 + doAssert a.lo.raw_data[1] == 2 + + + +block t2585: + type + RenderPass = object + state: ref int + RenderData = object + fb: int + walls: seq[RenderPass] + Mat2 = int + Vector2[T] = T + Pixels=int + + template use(fb: int, st: untyped): untyped = + echo "a ", $fb + st + echo "a ", $fb + + proc render(rdat: var RenderData; passes: var openArray[RenderPass]; proj: Mat2; + indexType = 1) = + for i in 0 ..< len(passes): + echo "blah ", repr(passes[i]) + + proc render2(rdat: var RenderData; screenSz: Vector2[Pixels]; proj: Mat2) = + use rdat.fb: + render(rdat, rdat.walls, proj, 1) + + + +block t4292: + template foo(s: string): string = s + proc variadicProc(v: varargs[string, foo]) = echo v[0] + variadicProc("a") + + + +block t2670: + template testTemplate(b: bool): typed = + when b: + var a = "hi" + else: + var a = 5 + echo a + testTemplate(true) + + + +block t4097: + var i {.compileTime.} = 2 + + template defineId(t: typedesc) = + const id {.genSym.} = i + static: inc(i) + proc idFor(T: typedesc[t]): int {.inline, raises: [].} = id + + defineId(int8) + defineId(int16) + + doAssert idFor(int8) == 2 + doAssert idFor(int16) == 3 + + + +block t5235: + template outer(body: untyped) = + template test(val: string) = + const SomeConst: string = val + echo SomeConst + body + + outer: + test("Hello, World!") + + +# bug #11941 +type X = object + e: int + +proc works(T: type X, v: auto): T = T(e: v) +template fails(T: type X, v: auto): T = T(e: v) + +var + w = X.works(42) + x = X.fails(42) + +echo x + +import mtempl5 + + +proc foo(): auto = + trap "foo": + echo "hey" + +discard foo() + + +# bug #4722 +type + IteratorF*[In] = iterator() : In {.closure.} + +template foof(In: untyped) : untyped = + proc ggg*(arg: IteratorF[In]) = + for i in arg(): + echo "foo" + + +iterator hello() : int {.closure.} = + for i in 1 .. 3: + yield i + +foof(int) +ggg(hello) + + +# bug #2586 +var z = 10'u8 +echo z < 9 # Works +echo z > 9 # Error: type mismatch + + +# bug #5993 +template foo(p: proc) = + var bla = 5 + p(bla) + +foo() do(t: var int): + discard + t = 5 + +proc bar(t: var int) = + t = 5 + +foo(bar) + +block: # bug #12595 + template test() = + let i = 42 + discard {i: ""} + + test() + +block: # bug #21920 + template t[T](): T = + discard + + t[void]() # Error: expression has no type: discard diff --git a/tests/template/template_pragmas.nim b/tests/template/template_pragmas.nim new file mode 100644 index 000000000..da532b010 --- /dev/null +++ b/tests/template/template_pragmas.nim @@ -0,0 +1,9 @@ +proc output_x:string {.compileTime.} = "x" + +template t = + const x = output_x() + let + bar {.exportc:"bar" & x.} = 100 + +static: + doAssert(compiles (t())) diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim new file mode 100644 index 000000000..2088b1739 --- /dev/null +++ b/tests/template/template_various.nim @@ -0,0 +1,406 @@ +discard """ +output: ''' +i2416 +33 +foo55 +foo8.0 +fooaha +bar7 +10 +4true +132 +20 +1 +-1 +4 +11 +26 +57 +-1-1-1 + 4 +4 + 4 + 11 +11 + 4 + 11 + 26 +26 + 4 + 11 + 26 + 57 +57 +-1-1-1 +''' +""" + +import macros + + + + +import i2416 +i2416() + + +import mcan_access_hidden_field +var myfoo = createFoo(33, 44) +echo myfoo.geta + + +import mgensym_generic_cross_module +foo(55) +foo 8.0 +foo "aha" +bar 7 + + + +block generic_templates: + type + SomeObj = object of RootObj + Foo[T, U] = object + x: T + y: U + + template someTemplate[T](): tuple[id: int32, obj: T] = + var result: tuple[id: int32, obj: T] = (0'i32, T()) + result + + let ret = someTemplate[SomeObj]() + + # https://github.com/nim-lang/Nim/issues/7829 + proc inner[T](): int = + discard + + template outer[A](): untyped = + inner[A]() + + template outer[B](x: int): untyped = + inner[B]() + + var i1 = outer[int]() + var i2 = outer[int](i1) + + # https://github.com/nim-lang/Nim/issues/7883 + template t1[T: int|int64](s: string): T = + var t: T + t + + template t1[T: int|int64](x: int, s: string): T = + var t: T + t + + var i3: int = t1[int]("xx") + +from strutils import contains + +block tgetast_typeliar: + proc error(s: string) = quit s + + macro assertOrReturn2(condition: bool; message: string) = + var line = condition.lineInfo() + result = quote do: + block: + if not likely(`condition`): + error("Assertion failed: " & $(`message`) & "\n" & `line`) + return + + macro assertOrReturn(condition: bool) = + var message : NimNode = newLit(condition.repr) + # echo message + result = getAst assertOrReturn2(condition, message) + # echo result.repr + let s = result.repr + doAssert """error("Assertion failed:""" in s + + proc point(size: int16): tuple[x, y: int16] = + # returns random point in square area with given `size` + assertOrReturn size > 0 + + + +type + MyFloat = object + val: float +converter to_myfloat(x: float): MyFloat {.inline.} = + MyFloat(val: x) + +block pattern_with_converter: + proc `+`(x1, x2: MyFloat): MyFloat = + MyFloat(val: x1.val + x2.val) + + proc `*`(x1, x2: MyFloat): MyFloat = + MyFloat(val: x1.val * x2.val) + + template optMul{`*`(a, 2.0)}(a: MyFloat): MyFloat = + a + a + + func floatMyFloat(x: MyFloat): MyFloat = + result = x * 2.0 + + func floatDouble(x: float): float = + result = x * 2.0 + + doAssert floatDouble(5) == 10.0 + + + + +block procparshadow: + template something(name: untyped) = + proc name(x: int) = + var x = x # this one should not be rejected by the compiler (#5225) + echo x + + something(what) + what(10) + + # bug #4750 + type + O = object + i: int + OP = ptr O + + template alf(p: pointer): untyped = + cast[OP](p) + + proc t1(al: pointer) = + var o = alf(al) + + proc t2(alf: pointer) = + var x = alf + var o = alf(x) + + + +block symchoicefield: + type Foo = object + len: int + + var f = Foo(len: 40) + + template getLen(f: Foo): int = f.len + + doAssert f.getLen == 40 + # This fails, because `len` gets the nkOpenSymChoice + # treatment inside the template early pass and then + # it can't be recognized as a field anymore + + + +import os, times +include "sunset.nimf" +block ttempl: + const + tabs = [["home", "index"], + ["news", "news"], + ["documentation", "documentation"], + ["download", "download"], + ["FAQ", "question"], + ["links", "links"]] + + + var i = 0 + for item in items(tabs): + var content = $i + var file: File + if open(file, changeFileExt(item[1], "html"), fmWrite): + write(file, sunsetTemplate(current=item[1], ticker="", content=content, + tabs=tabs)) + close(file) + else: + write(stdout, "cannot open file for writing") + inc(i) + + + +block ttempl4: + template `:=`(name, val: untyped) = + var name = val + + ha := 1 * 4 + hu := "ta-da" == "ta-da" + echo ha, hu + + + + +import mtempl5 +block ttempl5: + echo templ() + + #bug #892 + proc parse_to_close(value: string, index: int, open='(', close=')'): int = + discard + + # Call parse_to_close + template get_next_ident = + discard "{something}".parse_to_close(0, open = '{', close = '}') + + get_next_ident() + + #identifier expected, but found '(open|open|open)' + #bug #880 (also example in the manual!) + template typedef(name: untyped, typ: typedesc) = + type + `T name` {.inject.} = typ + `P name` {.inject.} = ref `T name` + + typedef(myint, int) + var x: PMyInt + + + +block templreturntype: + template `=~` (a: int, b: int): bool = false + var foo = 2 =~ 3 + +# bug #7117 +template parse9(body: untyped): untyped = + + template val9(arg: string): int {.inject.} = + var b: bool + if b: 10 + else: 20 + + body + +parse9: + echo val9("1") + + +block gensym1: + template x: untyped = -1 + template t1() = + template x: untyped {.gensym, redefine.} = 1 + echo x() # 1 + template t2() = + template x: untyped {.redefine.} = 1 # defaults to {.inject.} + echo x() # -1 injected x not available during template definition + t1() + t2() + +block gensym2: + let x,y,z = -1 + template `!`(xx,yy: typed): untyped = + template x: untyped {.gensym.} = xx + template y: untyped {.gensym.} = yy + let z = x + x + y + z + var + a = 1 + b = 2 + c = 3 + d = 4 + e = 5 + echo a ! b + echo a ! b ! c + echo a ! b ! c ! d + echo a ! b ! c ! d ! e + echo x,y,z + +block gensym3: + macro liftStmts(body: untyped): auto = + # convert + # template x: untyped {.gensym.} = + # let z = a + a + b + # echo z + # z + # to + # let z = a + a + b + # echo z + # template x: untyped {.gensym.} = + # z + #echo body.repr + body.expectKind nnkStmtList + result = newNimNode nnkStmtList + for s in body: + s.expectKind nnkTemplateDef + var sle = s[6] + while sle.kind == nnkStmtList: + doAssert(sle.len==1) + sle = sle[0] + if sle.kind == nnkStmtListExpr: + let n = sle.len + for i in 0..(n-2): + result.add sle[i] + var td = newNimNode nnkTemplateDef + for i in 0..5: + td.add s[i] + td.add sle[n-1] + result.add td + else: + result.add s + #echo result.repr + let x,y,z = -1 + template `!`(xx,yy: typed): untyped = + liftStmts: + template x: untyped {.gensym.} = xx + template y: untyped {.gensym.} = yy + let z = x + x + y + echo " ", z + z + var + a = 1 + b = 2 + c = 3 + d = 4 + e = 5 + echo a ! b + echo a ! b ! c + echo a ! b ! c ! d + echo a ! b ! c ! d ! e + echo x,y,z + +block: # issue #2465 + template t() = + template declX(str: string) {.gensym.} = + var x {.inject.} : string = str + + t() + doAssert not declared(declX) + doAssert not compiles(declX("a string")) + + template t2() = + template fooGensym() {.gensym.} = + echo 42 + + t2() + doAssert not declared(fooGensym) + doAssert not compiles(fooGensym()) + + +block identifier_construction_with_overridden_symbol: + # could use add, but wanna make sure it's an override no matter what + func examplefn = discard + func examplefn(x: int) = discard + + # the function our template wants to use + func examplefn1 = discard + + template exampletempl(n) = + # attempt to build a name using the overridden symbol "examplefn" + `examplefn n`() + + exampletempl(1) + +import typetraits + +block: # issue #4596 + type + T0 = object + T1 = object + + template printFuncsT() = + proc getV[A](a: typedesc[A]): string = + var s {. global .} = name(A) + return s + + printFuncsT() + + doAssert getV(T1) == "T1" + doAssert getV(T0) == "T0" + doAssert getV(T0) == "T0" + doAssert getV(T1) == "T1" diff --git a/tests/template/texponential_eval.nim b/tests/template/texponential_eval.nim new file mode 100644 index 000000000..b4e3faefb --- /dev/null +++ b/tests/template/texponential_eval.nim @@ -0,0 +1,51 @@ +# bug #1940 + +discard """ +nimout: ''' +=== +merge (A) with (B) +merge (A B) with (C) +merge (A B C) with (D) +merge (A B C D) with (E) +merge (A B C D E) with (F) +=== +''' + +output: "A B C D E F" +""" + +type SqlStmt = tuple + sql: string + parts: int + +proc sql(q: string): SqlStmt = + result.sql = q + result.parts = 1 + +template `&%%`(x, y: SqlStmt): SqlStmt = + const a = x + const b = y + + static: + #echo "some merge" + echo "merge (", a.sql, ") with (", b.sql, ")" + + + const newSql = a.sql & " " & b.sql + const newParts = a.parts + b.parts + + SqlStmt((sql: newSql, parts: newParts)) + +static: + echo "===" + +let c =(sql("A") &%% + sql("B")) &%% + sql("C") &%% + sql("D") &%% + sql("E") &%% + sql("F") +echo c.sql + +static: + echo "===" diff --git a/tests/template/tgenericparam.nim b/tests/template/tgenericparam.nim new file mode 100644 index 000000000..becf75d36 --- /dev/null +++ b/tests/template/tgenericparam.nim @@ -0,0 +1,93 @@ +block: # basic template generic parameter substitution + block: # issue #13527 + template typeNameTempl[T](a: T): string = $T + proc typeNameProc[T](a: T): string = $T + doAssert typeNameTempl(1) == typeNameProc(1) + doAssert typeNameTempl(true) == typeNameProc(true) + doAssert typeNameTempl(1.0) == typeNameProc(1.0) + doAssert typeNameTempl(1u8) == typeNameProc(1u8) + + template isDefault[T](a: T): bool = a == default(T) + doAssert isDefault(0.0) + + block: # issue #17240 + func to(c: int, t: typedesc[float]): t = discard + template converted[I, T](i: seq[I], t: typedesc[T]): seq[T] = + var result = newSeq[T](2) + result[0] = i[0].to(T) + result + doAssert newSeq[int](3).converted(float) == @[0.0, 0.0] + + block: # issue #6340 + type A[T] = object + v: T + proc foo(x: int): string = "int" + proc foo(x: typedesc[int]): string = "typedesc[int]" + template fooT(x: int): string = "int" + template fooT(x: typedesc[int]): string = "typedesc[int]" + proc foo[T](x: A[T]): (string, string) = + (foo(T), fooT(T)) + template fooT[T](x: A[T]): (string, string) = + (foo(T), fooT(T)) + var x: A[int] + doAssert foo(x) == fooT(x) + + block: # issue #20033 + template run[T](): T = default(T) + doAssert run[int]() == 0 + +import options, tables, typetraits + +block: # complex cases of above with imports + block: # issue #19576, complex case + type RegistryKey = object + key, val: string + var regKey = @[RegistryKey(key: "abc", val: "def")] + template findFirst[T](s: seq[T], pred: proc(x: T): bool): Option[T] = + var res = none(T) # important line + for x in s: + if pred(x): + res = some(x) + break + res + proc getval(searchKey: string): Option[string] = + let found = regKey.findFirst(proc (rk: RegistryKey): bool = rk.key == searchKey) + if found.isNone: none(string) + else: some(found.get().val) + doAssert getval("strange") == none(string) + doAssert getval("abc") == some("def") + block: # issue #19076 + block: # case 1 + var tested: Table[string,int] + template `[]`[V](t:Table[string,V],key:string):untyped = + $V + doAssert tested["abc"] == "int" + template `{}`[V](t:Table[string,V],key:string):untyped = + ($V, tables.`[]`(t, key)) + doAssert (try: tested{"abc"} except KeyError: ("not there", 123)) == ("not there", 123) + tables.`[]=`(tested, "abc", 456) + doAssert tested["abc"] == "int" + doAssert tested{"abc"} == ("int", 456) + block: # case 2 + type Foo[A,T] = object + t:T + proc init[A,T](f:type Foo,a:typedesc[A],t:T):Foo[A,T] = Foo[A,T](t:t) + template fromOption[A](o:Option[A]):auto = + when o.isSome: + Foo.init(A,35) + else: + Foo.init(A,"hi") + let op = fromOption(some(5)) + block: # issue #7461 + template p[T](): untyped = none(T) + doAssert p[int]() == none(int) + block: # issue #7995 + var res: string + template copyRange[T](dest: seq[T], destOffset: int) = + when supportsCopyMem(T): + res = "A" + else: + res = "B" + var a = @[1, 2, 3] + copyRange(a, 0) + doAssert res == "A" diff --git a/tests/template/tgensym_instantiationinfo.nim b/tests/template/tgensym_instantiationinfo.nim new file mode 100644 index 000000000..4b997ed6a --- /dev/null +++ b/tests/template/tgensym_instantiationinfo.nim @@ -0,0 +1,24 @@ +discard """ + action: "compile" +""" + +# bug #7937 + +template printError(error: typed) = + # Error: inconsistent typing for reintroduced symbol 'instInfo': previous type was: tuple[filename: string, line: int, column: int]; new type is: (string, int, int) + let instInfo {.gensym.} = instantiationInfo() + echo "Error at ", instInfo.filename, ':', instInfo.line, ": ", error + +# Removing this overload fixes the error +template someTemplate(someBool: bool, body) = + discard + +template someTemplate(body) = + body + +proc main() = + someTemplate: + printError("ERROR 1") + printError("ERROR 2") + +main() diff --git a/tests/template/tgensymhijack.nim b/tests/template/tgensymhijack.nim new file mode 100644 index 000000000..72ff3d495 --- /dev/null +++ b/tests/template/tgensymhijack.nim @@ -0,0 +1,37 @@ +# issue #23326 + +type Result*[E] = object + e*: E + +proc error*[E](v: Result[E]): E = discard + +template valueOr*[E](self: Result[E], def: untyped): int = + when E isnot void: + when false: + # Comment line below to make it work + template error(): E {.used, gensym.} = s.e + discard + else: + template error(): E {.used, inject.} = + self.e + + def + else: + def + + +block: + let rErr = Result[string](e: "a") + let rErrV = rErr.valueOr: + ord(error[0]) + +block: + template foo(x: static bool): untyped = + when x: + let a = 123 + else: + template a: untyped {.gensym.} = 456 + a + + doAssert foo(false) == 456 + doAssert foo(true) == 123 diff --git a/tests/template/tgensymregression.nim b/tests/template/tgensymregression.nim new file mode 100644 index 000000000..2a5dca934 --- /dev/null +++ b/tests/template/tgensymregression.nim @@ -0,0 +1,88 @@ +discard """ + output: '''[0.0, 0.0, 0.0] +[0.0, 0.0, 0.0, 0.0] +5050 +123''' +""" + +template mathPerComponent(op: untyped): untyped = + proc op*[N,T](v,u: array[N,T]): array[N,T] {.inline.} = + for i in 0 ..< len(result): + result[i] = `*`(v[i], u[i]) + +mathPerComponent(`***`) +# bug #5285 +when true: + if true: + var v1: array[3, float64] + var v2: array[3, float64] + echo repr(v1 *** v2) + + +proc foo(): void = + var v1: array[4, float64] + var v2: array[4, float64] + echo repr(v1 *** v2) + +foo() + +# bug #5383 +import sequtils + +proc zipWithIndex[A](ts: seq[A]): seq[(int, A)] = + toSeq(pairs(ts)) + +proc main = + discard zipWithIndex(@["foo", "bar"]) + discard zipWithIndex(@[1, 2]) + discard zipWithIndex(@[true, false]) + +main() + +# bug #5405 + +proc main2() = + let s = toSeq(1..100).foldL(a + b) + echo s + +main2() + +# bug #5467 +import macros + +converter int2string(x: int): string = $x + +template wrap(body: typed): untyped = + body + +macro makeProc() = + # Make a template tree + result = quote do: + proc someProc* = + wrap do: + let x = 123 + # Implicit conversion here + let s: string = x + echo s + +makeProc() + +someProc() + +# bug #12193 +import macros, strutils + +macro gen(T: typedesc): untyped = + let typeSym = getTypeImpl(T)[1] + let param = genSym(nskParam, "s") + let value = nnkBracketExpr.newTree(param, newIntLitNode(0)) + result = newProc( + name = ident"pack", + params = [typeSym, + newIdentDefs(param, nnkBracketExpr.newTree(ident"seq", ident"string"))], + body = newStmtList(newCall(typeSym, newCall(bindSym"parseInt", value))), + procType = nnkTemplateDef) + echo repr result + +gen(int) +let i = pack(@["2"]) diff --git a/tests/template/thygienictempl.nim b/tests/template/thygienictempl.nim new file mode 100644 index 000000000..9020c3e28 --- /dev/null +++ b/tests/template/thygienictempl.nim @@ -0,0 +1,22 @@ +discard """ +action: compile +""" + + +var + e = "abc" + +raise newException(IOError, e & "ha!") + +template t() = echo(foo) + +var foo = 12 +t() + + +template test_in(a, b, c: untyped): bool {.dirty.} = + var result {.gensym.}: bool = false + false + +when true: + assert test_in(ret2, "test", str_val) diff --git a/tests/template/tidentconcatenations.nim b/tests/template/tidentconcatenations.nim new file mode 100644 index 000000000..ddd2e49cc --- /dev/null +++ b/tests/template/tidentconcatenations.nim @@ -0,0 +1,31 @@ +type + Hash*[bits: static[int]] = object + data*: array[bits div 8, uint8] + +{.emit: """ + +void sha_256(void* input, int input_len, void* output, int output_len) {} +void sha_512(void* input, int input_len, void* output, int output_len) {} + +void keccak_256(void* input, int input_len, void* output, int output_len) {} +void keccak_512(void* input, int input_len, void* output, int output_len) {} + +""".} + +template defineKeccak(bits: untyped) = + proc `extKeccak bits`(output: pointer, outSize: csize_t, input: pointer, inputSize: csize_t) {.nodecl, importc: "keccak_" & astToStr(bits).} + +template defineSha(bits: static[int]) = + proc `extSha bits`(output: pointer, outSize: csize_t, input: pointer, inputSize: csize_t) {.nodecl, importc: "sha_" & astToStr(bits).} + +template defineHashProcs(bits) = + defineSha(bits) + defineKeccak(bits) + +defineHashProcs(256) +defineHashProcs(512) + +extSha256(nil, 0, nil, 0) +extSha512(nil, 0, nil, 0) +extKeccak256(nil, 0, nil, 0) +extKeccak512(nil, 0, nil, 0) diff --git a/tests/template/tinnerouterproc.nim b/tests/template/tinnerouterproc.nim new file mode 100644 index 000000000..56e0d02df --- /dev/null +++ b/tests/template/tinnerouterproc.nim @@ -0,0 +1,20 @@ +block: # #20002 + proc bar(x: int): int = 10 + template foo = + proc bar(x: int): int {.gensym.} = x + 2 + doAssert bar(3) == 5 + discard 3.bar # evaluates to 10 but only check if it compiles for now + block: + foo() + +block: # issue #23813 + template r(body: untyped) = + proc x() {.gensym.} = + body + template g() = + r: + let y = 0 + r: + proc y() = discard + y() + g() diff --git a/tests/template/tmethodcall.nim b/tests/template/tmethodcall.nim new file mode 100644 index 000000000..d209443c8 --- /dev/null +++ b/tests/template/tmethodcall.nim @@ -0,0 +1,24 @@ +# bug #5909 +type + Vec2[T] = tuple + x,y: T + Vec2f = Vec2[float32] + +proc vec2f(x,y: float): Vec2f = + result.x = x + result.y = y + +proc `-`[T](a,b: Vec2[T]): Vec2[T] = + result.x = a.x - b.x + result.y = a.y - b.y + +proc foo[T](a: Vec2[T]): Vec2[T] = + result = a + +block: + # this being called foo is a problem when calling .foo() + var foo = true + + let a = vec2f(1.0,0.0) + let b = vec2f(3.0,1.0) + let c = (a - b).foo() # breaks diff --git a/tests/template/tmixin_in_proc.nim b/tests/template/tmixin_in_proc.nim new file mode 100644 index 000000000..fede9290b --- /dev/null +++ b/tests/template/tmixin_in_proc.nim @@ -0,0 +1,22 @@ +discard """ + output: '''monkey''' +""" +# bug #5478 +template creature*(name: untyped) = + type + name*[T] = object + color: T + + proc `init name`*[T](c: T): name[T] = + mixin transform + transform() + +creature(Lion) + +type Monkey* = object +proc transform*() = + echo "monkey" + +var + m: Monkey + y = initLion(m) #this one failed to compile diff --git a/tests/template/tmodulealias.nim b/tests/template/tmodulealias.nim new file mode 100644 index 000000000..79b5ec9c6 --- /dev/null +++ b/tests/template/tmodulealias.nim @@ -0,0 +1,19 @@ +discard """ + disabled: true +""" + +when defined(windows): + import winlean +else: + import posix + +when defined(windows): + template orig: expr = + winlean +else: + template orig: expr = + posix + +proc socket(domain, typ, protocol: int): int = + result = orig.socket(ord(domain), ord(typ), ord(protocol))) + diff --git a/tests/template/tmore_regressions.nim b/tests/template/tmore_regressions.nim new file mode 100644 index 000000000..8b4b5fa4c --- /dev/null +++ b/tests/template/tmore_regressions.nim @@ -0,0 +1,44 @@ +discard """ +output: '''0 + +0.0''' +""" + +# bug #11494 +import macros + +macro staticForEach(arr: untyped, body: untyped): untyped = + result = newNimNode(nnkStmtList) + + arr.expectKind(nnkBracket) + for n in arr: + let b = copyNimTree(body) + result.add quote do: + block: + type it {.inject.} = `n` + `b` + +template forEveryMatchingEntity*() = + staticForEach([int, string, float]): + var a: it + echo a + +forEveryMatchingEntity() + + +# bug #11483 +proc main = + template first(body) = + template second: var int = + var o: int + var i = addr(o) + i[] + + body + + first: + second = 5 + second = 6 + +main() + diff --git a/tests/template/tnested.nim b/tests/template/tnested.nim new file mode 100644 index 000000000..81e416a76 --- /dev/null +++ b/tests/template/tnested.nim @@ -0,0 +1,38 @@ +block: # issue #22775 + proc h(c: int) = discard + template k(v: int) = + template p() = v.h() + p() + let a = @[0] + k(0 and not a[0]) + +block: # issue #22775 case 2 + proc h(c: int, q: int) = discard + template k(v: int) = + template p() = h(v, v) + p() + let a = [0] + k(0 and not a[0]) + +block: # issue #22775 minimal cases + proc h(c: int) = discard + template k(v: int) = + template p() = h(v) + p() + let a = [0] + k(not a[0]) + block: + k(-a[0]) + block: + proc f(x: int): int = x + k(f a[0]) + +block: # bracket assignment case of above tests + proc h(c: int) = discard + template k(v: int) = + template p() = h(v) + p() + var a = [0] + k(not (block: + a[0] = 1 + 1)) diff --git a/tests/template/tobjectdeclfield.nim b/tests/template/tobjectdeclfield.nim new file mode 100644 index 000000000..afce2cae8 --- /dev/null +++ b/tests/template/tobjectdeclfield.nim @@ -0,0 +1,21 @@ +block: # issue #16005 + var x = 0 + + block: + type Foo = object + x: float # ok + + template main() = + block: + type Foo = object + x: float # Error: cannot use symbol of kind 'var' as a 'field' + + main() + +block: # issue #19552 + template test = + type + test2 = ref object + reset: int + + test() diff --git a/tests/template/topensym.nim b/tests/template/topensym.nim new file mode 100644 index 000000000..2f930407b --- /dev/null +++ b/tests/template/topensym.nim @@ -0,0 +1,209 @@ +{.experimental: "openSym".} + +block: # issue #24002 + type Result[T, E] = object + func value[T, E](self: Result[T, E]): T {.inline.} = + discard + func value[T: not void, E](self: var Result[T, E]): var T {.inline.} = + discard + template unrecognizedFieldWarning = + doAssert value == 123 + let x = value + doAssert value == x + proc readValue(value: var int) = + unrecognizedFieldWarning() + var foo: int = 123 + readValue(foo) + +block: # issue #22605 for templates, normal call syntax + const error = "bad" + + template valueOr(self: int, def: untyped): untyped = + case false + of true: "" + of false: + template error: untyped {.used, inject.} = "good" + def + + template g(T: type): string = + var res = "ok" + let x = valueOr 123: + res = $error + "dummy" + res + + doAssert g(int) == "good" + + template g2(T: type): string = + bind error # use the bad version on purpose + var res = "ok" + let x = valueOr 123: + res = $error + "dummy" + res + + doAssert g2(int) == "bad" + +block: # issue #22605 for templates, method call syntax + const error = "bad" + + template valueOr(self: int, def: untyped): untyped = + case false + of true: "" + of false: + template error: untyped {.used, inject.} = "good" + def + + template g(T: type): string = + var res = "ok" + let x = 123.valueOr: + res = $error + "dummy" + res + + doAssert g(int) == "good" + + template g2(T: type): string = + bind error # use the bad version on purpose + var res = "ok" + let x = 123.valueOr: + res = $error + "dummy" + res + + doAssert g2(int) == "bad" + +block: # issue #22605 for templates, original complex example + type Xxx = enum + error + value + + type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + + template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + + proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + + template g(T: type): string = + var res = "ok" + let x = f().valueOr: + res = $error + 123 + res + + doAssert g(int) == "f" + + template g2(T: type): string = + bind error # use the bad version on purpose + var res = "ok" + let x = f().valueOr: + res = $error + 123 + res + + doAssert g2(int) == "error" + +block: # issue #23865 for templates + type Xxx = enum + error + value + + type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate: bool + else: + case oResultPrivate: bool + of false: + eResultPrivate: E + of true: + discard + else: + when E is void: + case oResultPrivate: bool + of false: + discard + of true: + vResultPrivate: T + else: + case oResultPrivate: bool + of false: + eResultPrivate: E + of true: + vResultPrivate: T + + func error[T, E](self: Result[T, E]): E = + ## Fetch error of result if set, or raise Defect + case self.oResultPrivate + of true: + when T isnot void: + raiseResultDefect("Trying to access error when value is set", self.vResultPrivate) + else: + raiseResultDefect("Trying to access error when value is set") + of false: + when E isnot void: + self.eResultPrivate + + template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + template g(T: type): string = + var res = "ok" + let x = f().valueOr: + res = $error + 123 + res + doAssert g(int) == "f" + +import std/sequtils + +block: # issue #15314 + var it: string + var nums = @[1,2,3] + + template doubleNums() = + nums.applyIt(it * 2) + + doubleNums() + doAssert nums == @[2, 4, 6] diff --git a/tests/template/topensymoverride.nim b/tests/template/topensymoverride.nim new file mode 100644 index 000000000..3d4bb59f1 --- /dev/null +++ b/tests/template/topensymoverride.nim @@ -0,0 +1,39 @@ +discard """ + matrix: "--skipParentCfg --filenames:legacyRelProj" +""" + +const value = "captured" +template fooOld(x: int, body: untyped): untyped = + let value {.inject.} = "injected" + body +template foo(x: int, body: untyped): untyped = + let value {.inject.} = "injected" + {.push experimental: "genericsOpenSym".} + body + {.pop.} + +proc old[T](): string = + fooOld(123): + return value +doAssert old[int]() == "captured" + +template oldTempl(): string = + block: + var res: string + fooOld(123): + res = value + res +doAssert oldTempl() == "captured" + +proc bar[T](): string = + foo(123): + return value +doAssert bar[int]() == "injected" + +template barTempl(): string = + block: + var res: string + foo(123): + res = value + res +doAssert barTempl() == "injected" diff --git a/tests/template/topensymwarning.nim b/tests/template/topensymwarning.nim new file mode 100644 index 000000000..0bbe0a9fb --- /dev/null +++ b/tests/template/topensymwarning.nim @@ -0,0 +1,60 @@ +discard """ + matrix: "--skipParentCfg --filenames:legacyRelProj" +""" + +type Xxx = enum + error + value + +type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + +template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + +proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + +template g(T: type): string = + var res = "ok" + let x = f().valueOr: + {.push warningAsError[IgnoredSymbolInjection]: on.} + # test spurious error + discard true + let _ = f + {.pop.} + res = $error #[tt.Warning + ^ a new symbol 'error' has been injected during template or generic instantiation, however 'error' [enumField declared in topensymwarning.nim(6, 3)] captured at the proc declaration will be used instead; either enable --experimental:openSym to use the injected symbol, or `bind` this captured symbol explicitly [IgnoredSymbolInjection]]# + 123 + res + +discard g(int) diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim new file mode 100644 index 000000000..b559c2d9e --- /dev/null +++ b/tests/template/tparams_gensymed.nim @@ -0,0 +1,405 @@ +discard """ +output: ''' +0 +1 +2 +3 +0 +1 +2 +3 +wth +3 +2 +1 +0 +(total: 6) +S1 +5 +abc +''' +""" +# bug #1915 + +import macros + +# Test that parameters are properly gensym'ed finally: + +template genNodeKind(kind, name: untyped) = + proc name*(children: varargs[NimNode]): NimNode {.compiletime.}= + result = newNimNode(kind) + for c in children: + result.add(c) + +genNodeKind(nnkNone, None) + + +# Test that generics in templates still work (regression to fix #1915) + +# bug #2004 + +type Something = object + +proc testA(x: Something) = discard + +template def(name: untyped) = + proc testB[T](reallyUniqueName: T) = + `test name`(reallyUniqueName) +def A + +var x: Something +testB(x) + + +# bug #2215 +# Test that templates in generics still work (regression to fix the +# regression...) + +template forStatic(index, slice, predicate: untyped) = + const a = slice.a + const b = slice.b + when a <= b: + template iteration(i: int) = + block: + const index = i + predicate + template iterateStartingFrom(i: int) = + when i <= b: + iteration i + iterateStartingFrom i + 1 + iterateStartingFrom a + +proc concreteProc(x: int) = + forStatic i, 0..3: + echo i + +proc genericProc(x: auto) = + forStatic i, 0..3: + echo i + +concreteProc(7) # This works +genericProc(7) # This doesn't compile + +import tables + +# bug #9476 +proc getTypeInfo*(T: typedesc): pointer = + var dummy: T + getTypeInfo(dummy) + + +macro implementUnary(op: untyped): untyped = + result = newStmtList() + + template defineTable(tableSymbol) = + var tableSymbol = initTable[pointer, pointer]() + let tableSymbol = genSym(nskVar, "registeredProcs") + result.add(getAst(defineTable(tableSymbol))) + + template defineRegisterInstantiation(tableSym, regTemplSym, instSym, op) = + template regTemplSym*(T: typedesc) = + let ti = getTypeInfo(T) + + proc instSym(xOrig: int): int {.gensym, cdecl.} = + let x {.inject.} = xOrig + op + + tableSym[ti] = cast[pointer](instSym) + + let regTemplSymbol = ident("registerInstantiation") + let instSymbol = ident("instantiation") + result.add(getAst(defineRegisterInstantiation( + tableSymbol, regTemplSymbol, instSymbol, op + ))) + + echo result.repr + + +implementUnary(): x*x + +registerInstantiation(int) +registerInstantiation(float) + +# bug #10192 +template nest(body) {.dirty.} = + template p1(b1: untyped) {.dirty, used.} = + template implp1: untyped {.dirty.} = b1 + template p2(b2: untyped) {.dirty, used.} = + template implp2: untyped {.dirty.} = b2 + + body + implp1 + implp2 + +template test() = + nest: + p1: + var foo = "bar" + p2: + doAssert(foo.len == 3) + +test() + +# regression found in PMunch's parser generator + +proc namedcall(arg: string) = + discard + +macro m(): untyped = + result = quote do: + (proc (arg: string) = + namedcall(arg = arg) + echo arg) + +let meh = m() +meh("wth") + + +macro foo(body: untyped): untyped = + result = body + +template baz(): untyped = + foo: + proc bar2(b: int): int = + echo b + if b > 0: b + bar2(b = b - 1) + else: 0 + echo (total: bar2(3)) + +baz() + +# bug #12121 +macro state_machine_enum(states: varargs[untyped]) = + result = nnkTypeSection.newTree( + nnkTypeDef.newTree( + nnkPragmaExpr.newTree(ident("State"), nnkPragma.newTree(ident("pure"))), + newEmptyNode(), + nnkEnumTy.newTree(newEmptyNode()) + ) + ) + + for s in states: + expectKind(s, nnkIdent) + result[0][2].add s + +template mystate_machine(body: untyped) = + state_machine_enum(S1, S2, S3) + var state_stack: seq[State] + template state_current(): State {.inject, used.} = + state_stack[^1] + template state_push(state_name) {.inject, used.} = + state_stack.add State.state_name + template state_pop(n = 1) {.inject, used.} = + state_stack.setLen(state_stack.len - n) + body + +mystate_machine: + state_push(S1) + echo state_current() + state_pop() + +# bug #15075 +block: #Doesn't work + template genGenTempl: untyped = + proc loop(locals: int) + proc loop(locals: int) = discard + genGenTempl() + let pool = loop + +block: #Doesn't work + macro genGenMacro: untyped = + quote do: + proc loop(locals: int) + proc loop(locals: int) = discard + genGenMacro() + let pool = loop + +block: #This works + proc loop(locals: int) + proc loop(locals: int) = discard + let pool = loop + +#Now somewhat recursive: +type Cont = ref object of RootObj + fn*: proc(c: Cont): Cont {.nimcall.} + +block: #Doesn't work + template genGenTempl(): untyped = + proc loop(locals: Cont): Cont + proc loop(locals: Cont): Cont = + return Cont(fn: loop) + proc doServer(): Cont = + return Cont(fn: loop) + genGenTempl() + discard doServer() + +block: #Doesn't work + macro genGenMacro(): untyped = + quote: + proc loop(locals: Cont): Cont + proc loop(locals: Cont): Cont = + return Cont(fn: loop) + proc doServer(): Cont = + return Cont(fn: loop) + genGenMacro() + discard doServer() + +block: #This works + proc loop(locals: Cont): Cont + proc loop(locals: Cont): Cont = + return Cont(fn: loop) + proc doServer(): Cont = + return Cont(fn: loop) + discard doServer() + +#And fully recursive: +block: #Doesn't work + template genGenTempl: untyped = + proc loop(locals: int) + proc loop(locals: int) = loop(locals) + genGenTempl() + let pool = loop + +block: #Doesn't work + macro genGenMacro: untyped = + quote do: + proc loop(locals: int) + proc loop(locals: int) = loop(locals) + genGenMacro() + let pool = loop + +block: #This works + proc loop(locals: int) + proc loop(locals: int) = loop(locals) + let pool = loop + +block: + template genAndCallLoop: untyped = + proc loop() {.gensym.} + proc loop() {.gensym.} = + discard + loop() + genAndCallLoop + +block: #Fully recursive and gensymmed: + template genGenTempl: untyped = + proc loop(locals: int) {.gensym.} + proc loop(locals: int) {.gensym.} = loop(locals) + let pool = loop + genGenTempl() + +block: #Make sure gensymmed symbol doesn't overwrite the forward decl + proc loop() + proc loop() = discard + template genAndCallLoop: untyped = + proc loop() {.gensym.} = + discard + loop() + genAndCallLoop() + +template genLoopDecl: untyped = + proc loop() +template genLoopDef: untyped = + proc loop() = discard +block: + genLoopDecl + genLoopDef + loop() +block: + proc loop() + genLoopDef + loop() +block: + genLoopDecl + proc loop() = discard + loop() + +block: #Gensymmed sym sharing forward decl + macro genGenMacro: untyped = + let sym = genSym(nskProc, "loop") + nnkStmtList.newTree( + newProc(sym, body = newEmptyNode()), + newCall(sym), + newProc(sym, body = newStmtList()), + ) + genGenMacro + +# inject pragma on params + +template test(procname, body: untyped): untyped = + proc procname(data {.inject.}: var int = 0) = + body + +test(hello): + echo data + data = 3 + +var data = 5 + +hello(data) + +# bug #5691 + +template bar(x: typed) = discard +macro barry(x: typed) = discard + +var a = 0 + +bar: + var a = 10 + +barry: + var a = 20 + +bar: + var b = 10 + +barry: + var b = 20 + +var b = 30 + +# template bar(x: static int) = discard +#You may think that this should work: +# bar((var c = 1; echo "hey"; c)) +# echo c +#But it must not! Since this would be incorrect: +# bar((var b = 3; const c = 1; echo "hey"; c)) +# echo b # <- b wouldn't exist + +discard not (let xx = 1; true) +discard xx + +template barrel(a: typed): untyped = a + +barrel: + var aa* = 1 + var bb = 3 + export bb + +# Test declaredInScope within params +template test1: untyped = + when not declaredInScope(thing): + var thing {.inject.}: int + +proc chunkedReadLoop = + test1 + test1 + +template test2: untyped = + when not not not declaredInScope(thing): + var thing {.inject.}: int + +proc chunkedReadLoop2 = + test2 + test2 + +test1(); test2() + +block: # bug #22846 + template foo2(x: proc (y: string)) = + let f = x + f("abc") + + foo2(proc (y: string) = echo y) + diff --git a/tests/template/tparamscope.nim b/tests/template/tparamscope.nim new file mode 100644 index 000000000..177c682cf --- /dev/null +++ b/tests/template/tparamscope.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "undeclared identifier: 'a'" + line: 10 +""" + + +template secondArg(a, b: typed): untyped = + b + +echo secondArg((var a = 1; 1), a) diff --git a/tests/template/tqualifiedident.nim b/tests/template/tqualifiedident.nim new file mode 100644 index 000000000..463b14ee7 --- /dev/null +++ b/tests/template/tqualifiedident.nim @@ -0,0 +1,8 @@ +block: # issue #19865 + template f() = discard default(system.int) + f() + +# issue #21221, same as above +type M = object +template r() = discard default(tqualifiedident.M) +r() diff --git a/tests/template/tqualifiedtype.nim b/tests/template/tqualifiedtype.nim new file mode 100644 index 000000000..6497af6ee --- /dev/null +++ b/tests/template/tqualifiedtype.nim @@ -0,0 +1,25 @@ +# issue #19866 + +# Switch module import order to switch which of last two +# doAsserts fails +import mqualifiedtype1 +import mqualifiedtype2 + +# this isn't officially supported but needed to point out the issue: +template f(moduleName: untyped): int = sizeof(`moduleName`.A) +template g(someType: untyped): int = sizeof(someType) + +# These are legitimately true. +doAssert sizeof(mqualifiedtype1.A) != sizeof(mqualifiedtype2.A) +doAssert g(mqualifiedtype1.A) != g(mqualifiedtype2.A) + +# Which means that this should not be true, but is in Nim 1.6 +doAssert f(`mqualifiedtype1`) != f(`mqualifiedtype2`) +doAssert f(mqualifiedtype1) != f(mqualifiedtype2) + +# These should be true, but depending on import order, exactly one +# fails in Nim 1.2, 1.6 and devel. +doAssert f(`mqualifiedtype1`) == g(mqualifiedtype1.A) +doAssert f(`mqualifiedtype2`) == g(mqualifiedtype2.A) +doAssert f(mqualifiedtype1) == g(mqualifiedtype1.A) +doAssert f(mqualifiedtype2) == g(mqualifiedtype2.A) diff --git a/tests/template/tredefinition.nim b/tests/template/tredefinition.nim new file mode 100644 index 000000000..8efc5ae2f --- /dev/null +++ b/tests/template/tredefinition.nim @@ -0,0 +1,13 @@ +discard """ + errormsg: "redefinition of 'a`gensym" + line: 9 +""" +# bug #10180 +proc f() = + template t() = + var a = 1 + var a = 2 + echo a + t() + +f() diff --git a/tests/template/tredefinition_override.nim b/tests/template/tredefinition_override.nim new file mode 100644 index 000000000..7ae232bba --- /dev/null +++ b/tests/template/tredefinition_override.nim @@ -0,0 +1,33 @@ +{.push warningAsError[ImplicitTemplateRedefinition]: on.} + +doAssert not (compiles do: + template foo(): int = 1 + template foo(): int = 2) +doAssert (compiles do: + template foo(): int = 1 + template foo(): int {.redefine.} = 2) +doAssert not (compiles do: + block: + template foo() = + template bar: string {.gensym.} = "a" + template bar: string {.gensym.} = "b" + foo()) +doAssert (compiles do: + block: + template foo() = + template bar: string {.gensym.} = "a" + template bar: string {.gensym, redefine.} = "b" + foo()) + +block: + template foo(): int = 1 + template foo(): int {.redefine.} = 2 + doAssert foo() == 2 +block: + template foo(): string = + template bar: string {.gensym.} = "a" + template bar: string {.gensym, redefine.} = "b" + bar() + doAssert foo() == "b" + +{.pop.} diff --git a/tests/template/tscope.nim b/tests/template/tscope.nim new file mode 100644 index 000000000..1eeebbdd4 --- /dev/null +++ b/tests/template/tscope.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "redefinition of 'x'" +""" + +var x = 1 +template quantity() = + # Causes internal error in compiler/sem.nim + proc unit*(x = 1.0): float = 12 + # Throws the correct error: redefinition of 'x' + #proc unit*(y = 1.0): float = 12 +quantity() +var x = 2 diff --git a/tests/template/tshadow.nim b/tests/template/tshadow.nim new file mode 100644 index 000000000..a4de71592 --- /dev/null +++ b/tests/template/tshadow.nim @@ -0,0 +1,29 @@ +discard """ + output: '''fish +fish''' +""" + +import macros + +block: + template init(initHook: proc(s: string)) = + proc dostuff = + var s = "fish" + initHook(s) + dostuff() + + init do(s: string): + echo s + +block: + macro init(initHook: proc(s: string)) = + result = newStmtList( + newProc(name = ident("dostuff"), body = newStmtList( + newVarStmt(ident("s"), newStrLitNode("fish")), + newCall(initHook, ident("s")) + )), + newCall("dostuff") + ) + + init proc(s: string) = + echo s diff --git a/tests/template/tsighash_regression.nim b/tests/template/tsighash_regression.nim new file mode 100644 index 000000000..f3a6b4833 --- /dev/null +++ b/tests/template/tsighash_regression.nim @@ -0,0 +1,8 @@ +discard """ +exitcode: 1 +outputsub: "0" +""" + +import tconfusinglocal + +fail "foo" diff --git a/tests/template/tstempl.nim b/tests/template/tstempl.nim new file mode 100644 index 000000000..649082041 --- /dev/null +++ b/tests/template/tstempl.nim @@ -0,0 +1,24 @@ +discard """ + output: '''global = levB, arg = levA, test = false +levB''' +""" + +# tstempl.nim +import strutils + +type + TLev = enum + levA, + levB + +var abclev = levB + +template tstLev(abclev: TLev) = + bind tstempl.abclev, `%` + writeLine(stdout, "global = $1, arg = $2, test = $3" % [ + $tstempl.abclev, $abclev, $(tstempl.abclev == abclev)]) + # evaluates to true, but must be false + + +tstLev(levA) +writeLine(stdout, $abclev) diff --git a/tests/template/ttempl2.nim b/tests/template/ttempl2.nim new file mode 100644 index 000000000..e84e0630b --- /dev/null +++ b/tests/template/ttempl2.nim @@ -0,0 +1,18 @@ +discard """ + errormsg: "undeclared identifier: \'b\'" + file: "ttempl2.nim" + line: 18 +""" +template declareInScope(x: untyped, t: typeDesc): untyped = + var x: t + +template declareInNewScope(x: untyped, t: typeDesc): untyped = + # open a new scope: + block: + var x: t + +declareInScope(a, int) +a = 42 # works, `a` is known here + +declareInNewScope(b, int) +b = 42 #ERROR_MSG undeclared identifier: 'b' diff --git a/tests/template/ttempl3.nim b/tests/template/ttempl3.nim new file mode 100644 index 000000000..17421cd87 --- /dev/null +++ b/tests/template/ttempl3.nim @@ -0,0 +1,83 @@ +discard """ +action: compile +""" + + +template withOpenFile(f: untyped, filename: string, mode: FileMode, + actions: untyped): untyped = + block: + # test that 'f' is implicitly 'injecting': + var f: File + if open(f, filename, mode): + try: + actions + finally: + close(f) + else: + quit("cannot open for writing: " & filename) + +withOpenFile(txt, "ttempl3.txt", fmWrite): + writeLine(txt, "line 1") + txt.writeLine("line 2") + +var + myVar: array[0..1, int] + +# Test zero argument template: +template ha: untyped = myVar[0] + +ha = 1 +echo(ha) + + +# Test identifier generation: +template prefix(name): untyped = `"hu" name` + +var `hu "XYZ"` = "yay" + +echo prefix(XYZ) + +template typedef(name: untyped, typ: typeDesc) {.dirty.} = + type + `T name`* = typ + `P name`* = ref `T name` + +typedef(myint, int) +var x: PMyInt + + +# Test UFCS + +type + Foo = object + arg: int + +proc initFoo(arg: int): Foo = + result.arg = arg + +template create(typ: typeDesc, arg: untyped): untyped = `init typ`(arg) + +var ff = Foo.create(12) + +echo ff.arg + + +import macros + +# bug #11494 +macro staticForEach(arr: untyped, body: untyped): untyped = + result = newNimNode(nnkStmtList) + arr.expectKind(nnkBracket) + for n in arr: + let b = copyNimTree(body) + result.add quote do: + block: + type it {.inject.} = `n` + `b` + +template forEveryMatchingEntity*() = + staticForEach([int, string, float]): + var a {.inject.}: it + echo a + +forEveryMatchingEntity() diff --git a/tests/template/tunderscore1.nim b/tests/template/tunderscore1.nim new file mode 100644 index 000000000..d74e5ba63 --- /dev/null +++ b/tests/template/tunderscore1.nim @@ -0,0 +1,11 @@ +discard """ + errormsg: "the special identifier '_' is ignored in declarations and cannot be used" +""" + +# issue #12094, #13804 + +template foo = + let _ = 1 + echo _ + +foo() diff --git a/tests/template/twhenintemplates.nim b/tests/template/twhenintemplates.nim new file mode 100644 index 000000000..6fded856d --- /dev/null +++ b/tests/template/twhenintemplates.nim @@ -0,0 +1,11 @@ +# bug #3670 + +template someTempl(someConst: bool) = + when someConst: + var a : int + if true: + when not someConst: + var a : int + a = 5 + +someTempl(true) diff --git a/tests/template/twrong_getast.nim b/tests/template/twrong_getast.nim new file mode 100644 index 000000000..1d158f7a5 --- /dev/null +++ b/tests/template/twrong_getast.nim @@ -0,0 +1,19 @@ +discard """ + errormsg: "expected a template that takes 3 arguments" + line: 16 +""" + +import macros + +template grainBlock(proxyTypeName: untyped, proxyProcs: untyped): typed = + discard + +var + proxyTypeName: string + proxyProcs: string + +macro foo(): untyped = + let x = getAst grainBlock(proxyTypeName, proxyProcs, proxyTypeName) + +foo() + diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim new file mode 100644 index 000000000..2d53d03f5 --- /dev/null +++ b/tests/template/twrongmapit.nim @@ -0,0 +1,30 @@ +discard """ + joinable: false +""" + +# bug #1562 +type Foo* {.pure, final.} = object + elt: float + +template defineOpAssign(T, op: untyped) = + proc `op`*(v: var T, w: T) {.inline.} = + for i in 0..1: + `op`(v.elt, w.elt) + +const ATTEMPT = 0 + +when ATTEMPT == 0: + # FAILS: defining `/=` with template calling template + # ERROR about sem.nim line 144 + template defineOpAssigns(T: untyped) = + mixin `/=` + defineOpAssign(T, `/=`) + + defineOpAssigns(Foo) + +# bug #1543 +import sequtils + +(var i = @[""];i).applyIt(it) +# now works: +doAssert i[0] == "" diff --git a/tests/template/twrongopensymchoice.nim b/tests/template/twrongopensymchoice.nim new file mode 100644 index 000000000..7a2bb48d3 --- /dev/null +++ b/tests/template/twrongopensymchoice.nim @@ -0,0 +1,24 @@ +discard """ + output: '''10''' +""" + +# bug #940 + +type + Foo* = ref object + b*: int + +proc new*(this: var Foo) = + assert this != nil + this.b = 10 + +proc new*(T: typedesc[Foo]): Foo = + system.new(result) + twrongopensymchoice.new(result) + +proc main = + var f = Foo.new() + echo f.b + +when true: + main() diff --git a/tests/template/twrongsymkind.nim b/tests/template/twrongsymkind.nim new file mode 100644 index 000000000..5fa618914 --- /dev/null +++ b/tests/template/twrongsymkind.nim @@ -0,0 +1,20 @@ +discard """ + errormsg: "cannot use symbol of kind 'var' as a 'param'" + line: 20 +""" + +# bug #3158 + +type + MyData = object + x: int + +template newDataWindow(data: ref MyData): untyped = + proc testProc(data: ref MyData) = + echo "Hello, ", data.x + testProc(data) + +var d: ref MyData +new(d) +d.x = 10 +newDataWindow(d) diff --git a/tests/template/utemplates.nim b/tests/template/utemplates.nim new file mode 100644 index 000000000..d70746578 --- /dev/null +++ b/tests/template/utemplates.nim @@ -0,0 +1,36 @@ +import unittest + +template t(a: int): string = "int" +template t(a: string): string = "string" + +block: # templates can be overloaded + check t(10) == "int" + check t("test") == "string" + +block: # previous definitions can be further overloaded or hidden in local scopes + template t(a: bool): string = "bool" + + check t(true) == "bool" + check t(10) == "int" + + template t(a: int): string = "inner int" + check t(10) == "inner int" + check t("test") == "string" + +block: # templates can be redefined multiple times + template customAssert(cond: bool, msg: string): typed {.dirty.} = + if not cond: fail(msg) + + template assertionFailed(body: untyped) {.dirty.} = + template fail(msg: string): typed {.redefine.} = + body + + assertionFailed: + check(msg == "first fail path") + + customAssert false, "first fail path" + + assertionFailed: + check(msg == "second fail path") + + customAssert false, "second fail path" |