From 0c8cefcbef66aa642b41ca20db1eb0f2a310d3e7 Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 13 Apr 2025 20:21:33 +0300 Subject: [PATCH] fix field setter fallback that never worked (#24871) refs https://forum.nim-lang.org/t/12785, refs #4711 The code was already there that when `propertyWriteAccess` returns `nil` (i.e. cannot find a setter), `semAsgn` turns the [LHS into a call and semchecks it](https://github.com/nim-lang/Nim/blob/1ef9a656d25f71dec6066e68ce6e9a518d5e9f16/compiler/semexprs.nim#L1941-L1948), meaning if a setter cannot be found a getter will be assigned to instead. However `propertyWriteAccess` never returned nil, because `semOverloadedCallAnalyseEffects` was not called with `efNoUndeclared` and so produced an error directly. So `efNoUndeclared` is passed to this call so this code works as intended. This fixes the issue described in #4711 which was closed because subscripts do not have the same behavior implemented. However we can implement this for subscripts as well (I have an implementation ready), it just changes the error message from the failed overloads of `[]=` to the failed overloads of `[]` for the LHS, which might be misleading but is consistent with the error messages for any other assignment. I can do this in this PR or another one. (cherry picked from commit 4d9e5e8b6d15107c3de5e7fc2b1c974437ef1cab) --- compiler/semexprs.nim | 2 +- tests/specialops/terrmsgs.nim | 3 +-- tests/specialops/tmismatch.nim | 2 +- tests/specialops/tsetterfallback1.nim | 24 ++++++++++++++++++++++++ tests/specialops/tsetterfallback2.nim | 8 ++++++++ 5 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 tests/specialops/tsetterfallback1.nim create mode 100644 tests/specialops/tsetterfallback2.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1dc952be51..55a58c7f04 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1787,7 +1787,7 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = result = newTreeI(nkCall, n.info, setterId, a[0], n[1]) result.flags.incl nfDotSetter let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1]) - result = semOverloadedCallAnalyseEffects(c, result, orig, {}) + result = semOverloadedCallAnalyseEffects(c, result, orig, {efNoUndeclared}) if result != nil: result = afterCallActions(c, result, nOrig, {}) diff --git a/tests/specialops/terrmsgs.nim b/tests/specialops/terrmsgs.nim index 081bca4510..534c0c4543 100644 --- a/tests/specialops/terrmsgs.nim +++ b/tests/specialops/terrmsgs.nim @@ -26,8 +26,7 @@ block: block: template `.=`(a: Foo, b: untyped, c: untyped) = b = c b.x = 123 #[tt.Error - ^ undeclared field: 'x=' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]# - # yeah it says x= but does it matter in practice + ^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]# block: template `()`(a: Foo, b: untyped, c: untyped) = echo "something" diff --git a/tests/specialops/tmismatch.nim b/tests/specialops/tmismatch.nim index 76c921b14a..7d0a4229dc 100644 --- a/tests/specialops/tmismatch.nim +++ b/tests/specialops/tmismatch.nim @@ -14,4 +14,4 @@ template `.=`*(flags: Flags, key: Flag, val: bool) = var flags: Flags flags.A = 123 #[tt.Error - ^ undeclared field: 'A=' for type tmismatch.Flags [type declared in tmismatch.nim(9, 5)]]# + ^ undeclared field: 'A' for type tmismatch.Flags [type declared in tmismatch.nim(9, 5)]]# diff --git a/tests/specialops/tsetterfallback1.nim b/tests/specialops/tsetterfallback1.nim new file mode 100644 index 0000000000..6a0b1104e3 --- /dev/null +++ b/tests/specialops/tsetterfallback1.nim @@ -0,0 +1,24 @@ +# issue #4711 + +type + Vec4 = object + x,y,z,w : float32 + + Vec3 = object + x,y,z : float32 + +proc `+=`(v0: var Vec3; v1: Vec3) = + v0.x += v1.x + v0.y += v1.y + v0.z += v1.z + +proc xyz(v: var Vec4): var Vec3 = + cast[ptr Vec3](v.x.addr)[] + +let tmp = Vec3(x: 1, y:2, z:3) +var dst = Vec4(x: 4, y:4, z:4, w:4) + +xyz(dst) = tmp # works +dst.xyz() = tmp # works +dst.xyz += tmp # works +dst.xyz = tmp # attempting to call undeclared routine `xyz=` diff --git a/tests/specialops/tsetterfallback2.nim b/tests/specialops/tsetterfallback2.nim new file mode 100644 index 0000000000..8458b2dc8b --- /dev/null +++ b/tests/specialops/tsetterfallback2.nim @@ -0,0 +1,8 @@ +# https://forum.nim-lang.org/t/12785 + +proc x(pt: var array[2, float]): var float = pt[0] + +var pt = [0.0, 0.0] +pt.x += 1.0 # <-- fine +x(pt) = 1.0 # <-- fine +pt.x = 1.0 # <-- does not compile