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..>` and `std::unique_ptr>`. +# +# Note: proc-side brackets-internal defaults (`proc foo[T, U = T]()`) are +# out of scope; the PR's logic targets type-side default substitution. +# A separate change would be needed for proc-side signature defaults. + +block: # #4086 type-side: object generic param default `seq[T]` + type Foo[T; U = seq[T]] = object + data: U + var f: Foo[int] + f.data.add 42 + doAssert f.data == @[42] + +block: # nested reference: U default uses T, V default uses U + type Foo[T; U = seq[T]; V = seq[U]] = object + data: V + var f: Foo[int] + f.data.add @[1, 2] + doAssert f.data == @[@[1, 2]] + +block: # type-side direct: U = T + type Foo[T; U = T] = object + a: T + b: U + var f: Foo[int] + f.a = 1 + f.b = 2 + doAssert f.b is int + +block: # type-side compound: U = ref T + type Foo[T; U = ref T] = object + p: U + var f: Foo[int] + f.p = new(int) + f.p[] = 9 + doAssert f.p[] == 9 + +block: # type-side compound: U = array[3, T] + type Foo[T; U = array[3, T]] = object + arr: U + var f: Foo[int] + f.arr[0] = 10 + f.arr[2] = 30 + doAssert f.arr[0] == 10 + doAssert f.arr[2] == 30 + +block: # #4086 original: all params defaulted, invocation with no args + type Foo[T = int] = object + x: T + var f: Foo + f.x = 42 + doAssert f.x == 42 + +block: # alias of a defaulted instantiation + type Foo[T; U = T] = object + a: T + b: U + type IntFoo = Foo[int] + var f: IntFoo + f.a = 1 + f.b = 2 + doAssert f.b is int + +block: # distinct of a defaulted instantiation + type Foo[T; U = seq[T]] = object + data: U + type DistFoo = distinct Foo[int] + var f: DistFoo + Foo[int](f).data.add 7 + doAssert Foo[int](f).data == @[7] + +block: # union constraint + default referencing T (review request) + type Foo[T; U: seq[T]|Deque[T] = seq[T]] = object + data: U + var f: Foo[int] + f.data.add 42 + doAssert f.data is seq[int] + doAssert f.data == @[42] + +block: # typeclass constraint + concrete-type default (review request) + type Foo[T: SomeInteger = int] = object + x: T + var f: Foo + f.x = 7 + doAssert f.x is int + var g: Foo[int64] + g.x = 9'i64 + doAssert g.x is int64 + +block: # concept constraint + default (review request) + type HasLen = concept x + x.len is int + type Foo[T: HasLen = string] = object + val: T + var f: Foo + f.val = "hi" + doAssert f.val.len == 2 + var g: Foo[seq[int]] + g.val = @[1, 2, 3] + doAssert g.val.len == 3