fixes #26132; =destroy should accept non-parametrized generic (#26142)

fixes  #26132
This commit is contained in:
ringabout
2026-08-29 20:40:46 +08:00
committed by GitHub
parent f897fe8c29
commit 802bcf5a2d
2 changed files with 138 additions and 75 deletions

View File

@@ -2160,47 +2160,54 @@ proc checkedForDestructor(t: PType): bool =
return true
result = false
proc whereToBindTypeHook(c: PContext; t: PType): PType =
proc normalizeTypeHook(t: PType; markAsgn = false): PType =
result = t
while true:
if result.kind in {tyGenericBody, tyGenericInst}: result = result.skipModifier
elif result.kind == tyGenericInvocation: result = result[0]
else: break
if markAsgn:
incl(result, tfHasAsgn)
if result.kind == tyCompositeTypeClass and result.base.kind == tyGenericBody:
result = result.base
elif result.kind in {tyGenericBody, tyGenericInst}:
result = result.skipModifier
elif result.kind == tyGenericInvocation:
result = result.genericHead
else:
break
proc whereToBindTypeHook(c: PContext; t: PType): PType =
result = normalizeTypeHook(t)
if result.kind in {tyObject, tyDistinct, tySequence, tyString}:
result = canonType(c, result)
proc bindHookToType(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp;
typeToBind: PType): bool =
var obj = typeToBind
if obj.kind notin {tyObject, tyDistinct, tySequence, tyString}:
return false
obj = canonType(c, obj)
let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
discard "forward declared hook"
elif ao.isNil and not checkedForDestructor(obj):
setAttachedOp(c.graph, c.module.position, obj, op, s)
else:
prevDestructor(c, op, ao, obj, n.info)
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
result = true
proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
let t = s.typ
var noError = false
let cond = t.len == 2 and t.returnType != nil
if cond:
var obj = t.firstParamType
while true:
incl(obj, tfHasAsgn)
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
else: break
var obj = normalizeTypeHook(t.firstParamType, markAsgn = true)
let res = normalizeTypeHook(t.returnType)
var res = t.returnType
while true:
if res.kind in {tyGenericBody, tyGenericInst}: res = res.skipModifier
elif res.kind == tyGenericInvocation: res = res.genericHead
else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, res):
obj = canonType(c, obj)
let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
discard "forward declared destructor"
elif ao.isNil and not checkedForDestructor(obj):
setAttachedOp(c.graph, c.module.position, obj, op, s)
else:
prevDestructor(c, op, ao, obj, n.info)
noError = true
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
if sameType(obj, res):
noError = bindHookToType(c, s, n, op, obj)
if not noError and sfSystemModule notin s.owner.flags:
localError(c.config, n.info, errGenerated,
@@ -2230,25 +2237,8 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
t.len >= 2 and t.returnType == nil
if cond:
var obj = t.firstParamType.skipTypes({tyVar})
while true:
incl(obj, tfHasAsgn)
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
obj = canonType(c, obj)
let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
discard "forward declared destructor"
elif ao.isNil and not checkedForDestructor(obj):
setAttachedOp(c.graph, c.module.position, obj, op, s)
else:
prevDestructor(c, op, ao, obj, n.info)
noError = true
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
var obj = normalizeTypeHook(t.firstParamType.skipTypes({tyVar}), markAsgn = true)
noError = bindHookToType(c, s, n, op, obj)
if not noError and sfSystemModule notin s.owner.flags:
case op
of attachedTrace:
@@ -2315,35 +2305,12 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead")
let t = s.typ
if t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar:
var obj = t.firstParamType.elementType
while true:
incl(obj, tfHasAsgn)
if obj.kind == tyGenericBody: obj = obj.skipModifier
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
else: break
var objB = t[2]
while true:
if objB.kind == tyGenericBody: objB = objB.skipModifier
elif objB.kind in {tyGenericInvocation, tyGenericInst}:
objB = objB.genericHead
else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
var obj = normalizeTypeHook(t.firstParamType.elementType, markAsgn = true)
let objB = normalizeTypeHook(t[2])
if sameType(obj, objB):
# attach these ops to the canonical tySequence
obj = canonType(c, obj)
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink
let ao = getAttachedOp(c.graph, obj, k)
if ao == s:
discard "forward declared op"
elif ao.isNil and not checkedForDestructor(obj):
setAttachedOp(c.graph, c.module.position, obj, k, s)
else:
prevDestructor(c, k, ao, obj, n.info)
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
return
if bindHookToType(c, s, n, k, obj): return
if sfSystemModule notin s.owner.flags:
localError(c.config, n.info, errGenerated,
"signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)")

View File

@@ -166,3 +166,99 @@ type Vector*[T] = object
# proc `=destroy`*(x: var Vector[int]) = discard # this will remove error
proc `=destroy`*[T](x: var Vector[T]) = discard
var a: Vector[int] # Error: unresolved generic parameter
# issue #26132
block:
type UnparameterizedGeneric[T] = object
proc `=destroy`(x: var UnparameterizedGeneric) = discard
proc `=wasMoved`(x: var UnparameterizedGeneric) = discard
proc `=trace`(x: var UnparameterizedGeneric; env: pointer) = discard
var x: UnparameterizedGeneric[int]
discard x
# Exercise every type-bound hook with the generic parameter omitted.
block:
type
Generic[T] = object
value: T
var destroys, moves, traces, copies, sinks, dups: int
proc `=destroy`(x: var Generic) = inc destroys
proc `=wasMoved`(x: var Generic) =
inc moves
x.value = default(typeof(x.value))
proc `=trace`(x: var Generic; env: pointer) = inc traces
proc `=copy`(dest: var Generic; src: Generic) =
inc copies
dest.value = src.value
proc `=sink`(dest: var Generic; src: Generic) =
inc sinks
dest.value = src.value
proc `=dup`(src: Generic): Generic =
inc dups
Generic(value: src.value)
proc deepCopy(src: ref Generic): ref Generic = src
proc exercise[T]() =
var first = Generic[T](value: default(T))
var second = Generic[T](value: default(T))
second = first
doAssert second.value == first.value
second = Generic[T](value: default(T))
doAssert second.value == default(T)
`=trace`(first, nil)
`=wasMoved`(first)
let implicitDuplicate = first
discard implicitDuplicate
let duplicate = `=dup`(first)
discard duplicate
let original = new(Generic[T])
doAssert deepCopy(original) == original
exercise[string]()
exercise[int]()
exercise[seq[int]]()
doAssert copies > 0
doAssert sinks > 0
doAssert dups > 0
doAssert moves > 0
doAssert traces > 0
doAssert destroys > 0
block:
type GenericDistinct[T] = distinct Generic[T]
proc `=destroy`(x: var GenericDistinct) = discard
proc `=wasMoved`(x: var GenericDistinct) = discard
proc `=trace`(x: var GenericDistinct; env: pointer) = discard
proc `=copy`(dest: var GenericDistinct; src: GenericDistinct) = discard
proc `=sink`(dest: var GenericDistinct; src: GenericDistinct) = discard
proc `=dup`(src: GenericDistinct): GenericDistinct = src
proc deepCopy(src: ref GenericDistinct): ref GenericDistinct = src
var first = GenericDistinct[string](Generic[string](value: "first"))
var second = GenericDistinct[string](Generic[string](value: "second"))
second = first
second = GenericDistinct[string](Generic[string](value: "third"))
`=trace`(first, nil)
`=wasMoved`(first)
let moved = move(first)
let duplicate = `=dup`(moved)
discard duplicate
let original = new(GenericDistinct[string])
doAssert deepCopy(original) == original
block:
type GenericPair[A, B] = object
left: A
right: B
proc `=destroy`(x: var GenericPair) = discard
var pair = GenericPair[int, string](left: 42, right: "pair")
discard pair