mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
don't typecheck untyped + allow void typed template param default values (#24219)
Previously, the compiler never differentiated between `untyped`/`typed` argument default values and other types, it considered any parameter with a type as typed and called `semExprWithType`, which both typechecked it and disallowed `void` expressions. Now, we perform no typechecking at all on `untyped` template param default values, and call `semExpr` instead for `typed` params, which allows expressions with `void` type.
This commit is contained in:
@@ -1372,14 +1372,25 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
"either use ';' (semicolon) or explicitly write each default value")
|
||||
message(c.config, a.info, warnImplicitDefaultValue, msg)
|
||||
block determineType:
|
||||
if kind == skTemplate and hasUnresolvedArgs(c, def):
|
||||
# template default value depends on other parameter
|
||||
# don't do any typechecking
|
||||
def.typ = makeTypeFromExpr(c, def.copyTree)
|
||||
break determineType
|
||||
var canBeVoid = false
|
||||
if kind == skTemplate:
|
||||
if typ != nil and typ.kind == tyUntyped:
|
||||
# don't do any typechecking or assign a type for
|
||||
# `untyped` parameter default value
|
||||
break determineType
|
||||
elif hasUnresolvedArgs(c, def):
|
||||
# template default value depends on other parameter
|
||||
# don't do any typechecking
|
||||
def.typ = makeTypeFromExpr(c, def.copyTree)
|
||||
break determineType
|
||||
elif typ != nil and typ.kind == tyTyped:
|
||||
canBeVoid = true
|
||||
let isGeneric = isCurrentlyGeneric()
|
||||
inc c.inGenericContext, ord(isGeneric)
|
||||
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ)
|
||||
if canBeVoid:
|
||||
def = semExpr(c, def, {efDetermineType, efAllowSymChoice}, typ)
|
||||
else:
|
||||
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ)
|
||||
dec c.inGenericContext, ord(isGeneric)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
@@ -1399,7 +1410,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
|
||||
typ.flags.incl tfCheckedForDestructor
|
||||
|
||||
elif def.typ.kind != tyFromExpr:
|
||||
elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void
|
||||
# if def.typ != nil and def.typ.kind != tyNone:
|
||||
# example code that triggers it:
|
||||
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
||||
|
||||
@@ -13,3 +13,44 @@ block: # issue #23506
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user