diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 465276ffc20a2..6033e29c0be4a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -853,6 +853,16 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) hasUserSpecifiedType = true + # Issue #4086: `var f: Foo` for `type Foo[T = int]` auto-expands to + # `Foo[int]` when every generic param has a default. Restricted to + # var/let/const declarations so it does not affect type-level + # computations like `arity(SomeGeneric)` in template/typetraits + # contexts where bare generic-body references are intentional. + if typ != nil and typ.kind == tyGenericBody and typ.sym != nil: + let auto = tryGenericBodyDefaultInvocation(c, + newSymNode(typ.sym, a[^2].info), typ.sym, nil) + if auto != nil: + typ = auto var typFlags: TTypeAllowedFlags = {} @@ -1009,6 +1019,16 @@ proc semConst(c: PContext, n: PNode): PNode = if a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) hasUserSpecifiedType = true + # Issue #4086: `var f: Foo` for `type Foo[T = int]` auto-expands to + # `Foo[int]` when every generic param has a default. Restricted to + # var/let/const declarations so it does not affect type-level + # computations like `arity(SomeGeneric)` in template/typetraits + # contexts where bare generic-body references are intentional. + if typ != nil and typ.kind == tyGenericBody and typ.sym != nil: + let auto = tryGenericBodyDefaultInvocation(c, + newSymNode(typ.sym, a[^2].info), typ.sym, nil) + if auto != nil: + typ = auto var typFlags: TTypeAllowedFlags = {} diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8009f7293c61e..534ece3ddb384 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1736,6 +1736,33 @@ proc containsGenericInvocationWithForward(n: PNode): bool = return true return false +proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType + +proc tryGenericBodyDefaultInvocation*(c: PContext, n: PNode, s: PSym, + prev: PType): PType = + ## Issue #4086 sub-case: `type Foo[T = int]; var f: Foo` is sugar for + ## `var f: Foo[int]` when every generic param has a default. Synthesize + ## a `Foo[default1, default2, ...]` bracket node and dispatch to the + ## existing `semGeneric` so the default-substitution machinery (added + ## for issues #4086 / #9355) handles cascading and parameter-referencing + ## defaults uniformly. + result = nil + if s.typ == nil: return + let body = s.typ.skipTypes({tyAlias}) + if body.kind != tyGenericBody or body.len < 2: return + for i in 0..