I’m getting a "type mismatch" when compiling my code, and I cannot see what I’m doing wrong. Since the code is somewhat large, here is a summarized version:
# MODULE atomiks
type
VolatilePtr*[T] = distinct ptr T
template declVolatile*(name: untyped, T: typedesc, init: T) {.dirty.} =
## Defines an hidden volatile value, with an initial value, and a pointer to it
var `volatile XXX name XXX value` {.global,volatile.} = init
let `name` {.global.} = cast[VolatilePtr[T]](addr `volatile XXX name XXX value`)
# ...
proc atomicIncRelaxed*[T: AtomType](p: VolatilePtr[T], x: T = 1): T =
## Increments a volatile (32/64 bit) value, and returns the new value.
## Performed in RELAXED/No-Fence memory-model.
## Will only compile on Windows 8+!
let pp = cast[pointer](p)
when sizeof(T) == 8:
cast[T](interlockedExchangeAddNoFence64(pp, cast[int64](x)))
elif sizeof(T) == 4:
cast[T](interlockedExchangeAddNoFence32(pp, cast[int32](x)))
else:
static: assert(false, "invalid parameter size: " & $sizeof(T))
# MODULE kueues
type
MsgSeqID* = int32
declVolatile(myProcessGlobalSeqID, MsgSeqID, MsgSeqID(0))
# Current (global) per Process message sequence ID.
proc seqidProvider(): MsgSeqID =
## Generates a message sequence ID for the Thread.
result = atomicIncRelaxed(myProcessGlobalSeqID) # LINE NO 213
The line with atomicIncRelaxed() doesn’t compile with this message:
kueues.nim(213, 14) Error: type mismatch: got (int) but expected 'MsgSeqID = int32'
I think that the type of “myProcessGlobalSeqID” should be “VolatilePtr[MsgSeqID]”, therefore the call to “atomicIncRelaxed(myProcessGlobalSeqID)” should receive a “VolatilePtr[MsgSeqID]” as input parameter, and return a “MsgSeqID” as output, since I cast the result of "interlockedExchangeAddNoFenceXX()" to "T". Since “result” is of type “MsgSeqID”, everything should be fine, IMO.
EDIT:
If I add a cast, just so it compiles, like this:
result = MsgSeqID(atomicIncRelaxed(myProcessGlobalSeqID))
I get this warning:
warning C4133: 'function': incompatible types - from 'NI32 *' to 'NI *'
And the generated code at that location looks like:
NI32 volatile volatileXXXmyProcessGlobalSeqIDXXXvalue_E9bTOMWoAg8GLV5f71h9b3tg;
NI32* myProcessGlobalSeqID_08rTXbtOEJJH36WoNR65hw;
#...
N_NIMCALL(NI32, seqidProvider_C9apW3EupUtW1ohORfpWXXw)(void) {
NI32 result;
NI T1_;
nimfr_("seqidProvider", "kueues.nim");
result = (NI32)0;
nimln_(213, "kueues.nim");
T1_ = (NI)0;
T1_ = atomicIncRelaxed_7kAYyH5nySTDnWgC39bO1AQ(myProcessGlobalSeqID_08rTXbtOEJJH36WoNR65hw, ((NI) 1));
result = ((NI32)chckRange(T1_, ((NI32) (-2147483647 -1)), ((NI32) 2147483647)));
popFrame();
return result;
}
# Replace:
proc atomicIncRelaxed*[T: AtomType](p: VolatilePtr[T], x: T = 1): T
# with:
proc atomicIncRelaxed*[T: AtomType](p: VolatilePtr[T], x: T = 1.int32): T
Note it will still work for int64 thanks to the conversion.
The reason of this problem is that int32 is int at the same time. Because you used 1, which is int, and the VolatilePtr[int32] could be VolatilePtr[int] as well (thanks to int=int32), I take it the compiler was happy to assume T=int. When you use 1.int32 explicitly, either in the call or default value, it does the opposite: if it takes VolatilePtr[int], it assumes it is VolatilePtr[int32] (because it matched the default 1.int32) and happily assigns an int32 result to an int.
So the general conclusion is: type equality is one-way in some contexts.
@Udiknedormin I can't say 100% for sure, because now I get some error that it cannot find the required "Interlocked" function (maybe wrong version of Visual Studio installed?), but at least the original error message is gone, so I think your solution worked. :)
EDIT:
I have now found the definition of InterlockedExchangeAddNoFence() in winnt.h (and In fact, what I needed was InterlockedAddNoFence(), which is also there). And it looks like this:
#define InterlockedExchangeAddNoFence _InterlockedExchangeAdd
Argh! The "no fence" is a joke!