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:
metagn
2024-10-03 21:38:42 +03:00
committed by GitHub
parent d98ef312f0
commit 4eed341ba5
2 changed files with 59 additions and 7 deletions

View File

@@ -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)

View File

@@ -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"