From 2529f33760366ab59d55dce04fef2937673adcc9 Mon Sep 17 00:00:00 2001 From: metagn Date: Wed, 4 Dec 2024 07:16:37 +0300 Subject: [PATCH] remove inserted derefs for ref object fields when transforming to dot call (#24498) fixes #24492 Kind of a goofy way of doing this, but we count how many derefs were used for the first parameter before calling `builtinFieldAccess`, then count after, and if there are more now than before, we remove the added derefs. I thought maybe getting rid of #18298 would simplify it but maybe this would still be the best way. For better encapsulation we could make `dotTransformation` take an `nOrig` param instead but this would be less efficient since it would need a copy, though `semAsgn` already makes one. --- compiler/semexprs.nim | 28 +++++++++++++++++++++----- tests/objects/trefobjfieldoverload.nim | 6 ++++++ 2 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 tests/objects/trefobjfieldoverload.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index bac00bbee1..916cedab6a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1680,29 +1680,46 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = result = tryReadingGenericParam(c, n, i, t) flags.incl efCannotBeDotCall -proc dotTransformation(c: PContext, n: PNode): PNode = +proc hiddenDerefDepth(n: PNode): int = + result = 0 + var n = n + while n.kind == nkHiddenDeref: + inc result + n = n[0] + +proc dotTransformation(c: PContext, n: PNode, initialDerefs: int): PNode = + var root = n[0] + let currentDerefs = hiddenDerefDepth(root) + if currentDerefs > initialDerefs: + # hidden derefs were inserted by `builtinFieldAccess` for fields of + # `ref object` etc. + # undo the derefs for overload resolution + for _ in initialDerefs ..< currentDerefs: + root = root[0] + root = copyTree(root) if isSymChoice(n[1]) or # generics usually leave field names as symchoices, but not types (n[1].kind == nkSym and n[1].sym.kind == skType): result = newNodeI(nkDotCall, n.info) result.add n[1] - result.add copyTree(n[0]) + result.add root else: var i = considerQuotedIdent(c, n[1], n) result = newNodeI(nkDotCall, n.info) result.flags.incl nfDotField result.add newIdentNode(i, n[1].info) - result.add copyTree(n[0]) + result.add root proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # this is difficult, because the '.' is used in many different contexts # in Nim. We first allow types in the semantic checking. var f = flags - {efIsDotCall} + let initialDerefDepth = hiddenDerefDepth(n[0]) result = builtinFieldAccess(c, n, f) if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and efIsDotCall in flags and callOperator notin c.features and efCannotBeDotCall notin f): - result = dotTransformation(c, n) + result = dotTransformation(c, n, initialDerefDepth) proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = result = newNodeI(nkCall, n.info) @@ -2004,13 +2021,14 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # --> `f=` (r, x) let nOrig = n.copyTree var flags = {efLValue} + let initialDerefDepth = hiddenDerefDepth(a[0]) a = builtinFieldAccess(c, a, flags) if a == nil: a = propertyWriteAccess(c, n, nOrig, n[0]) if a != nil: return a # we try without the '='; proc that return 'var' or macros are still # possible: - a = dotTransformation(c, n[0]) + a = dotTransformation(c, n[0], initialDerefDepth) if a.kind == nkDotCall: a.transitionSonsKind(nkCall) a = semExprWithType(c, a, {efLValue}) diff --git a/tests/objects/trefobjfieldoverload.nim b/tests/objects/trefobjfieldoverload.nim new file mode 100644 index 0000000000..ebac10daca --- /dev/null +++ b/tests/objects/trefobjfieldoverload.nim @@ -0,0 +1,6 @@ +block: # issue #24492 + type MyType = ref object + a: int + + proc a(val: MyType, i: int) = discard + MyType().a(100)