diff --git a/tests/ic/mtoffcodec.nim b/tests/ic/mtoffcodec.nim new file mode 100644 index 0000000000..22037afdce --- /dev/null +++ b/tests/ic/mtoffcodec.nim @@ -0,0 +1,3 @@ +# Helper for ttypeoffer.nim: the extra overload that flips `compiles(toSszType(Gwei))`. +import mtoffgwei +template toSszType*(v: Gwei): uint64 = uint64(v) diff --git a/tests/ic/mtoffgwei.nim b/tests/ic/mtoffgwei.nim new file mode 100644 index 0000000000..04a395213a --- /dev/null +++ b/tests/ic/mtoffgwei.nim @@ -0,0 +1,2 @@ +# Helper for ttypeoffer.nim: a distinct basic type. +type Gwei* = distinct uint64 diff --git a/tests/ic/mtoffssz.nim b/tests/ic/mtoffssz.nim new file mode 100644 index 0000000000..6ee77cac94 --- /dev/null +++ b/tests/ic/mtoffssz.nim @@ -0,0 +1,10 @@ +# Helper for ttypeoffer.nim: a generic container whose hash-array bound depends +# on a `mixin toSszType` resolved at the instantiation site (cf. ssz dataPerChunk). +template perChunk*(T: type): int = + mixin toSszType + when compiles(toSszType(default(T))): 4 else: 1 + +type + HA*[N: static int; T] = object + data*: array[N, T] + hashes*: array[N div perChunk(T), uint64] diff --git a/tests/ic/mtoffstate.nim b/tests/ic/mtoffstate.nim new file mode 100644 index 0000000000..beba18e387 --- /dev/null +++ b/tests/ic/mtoffstate.nim @@ -0,0 +1,5 @@ +# Helper for ttypeoffer.nim ("datatypes" analog): instantiates HA[64,Gwei] WITHOUT +# the codec in scope -> perChunk=1. This is the instance that must be shared. +import mtoffssz, mtoffgwei +type StateA* = object + field*: HA[64, Gwei] diff --git a/tests/ic/mtoffuser.nim b/tests/ic/mtoffuser.nim new file mode 100644 index 0000000000..717fa7633d --- /dev/null +++ b/tests/ic/mtoffuser.nim @@ -0,0 +1,10 @@ +# Helper for ttypeoffer.nim ("db_immutable" analog): imports the codec (so +# `toSszType(Gwei)` is visible -> perChunk=4) and re-instantiates HA[64,Gwei]. +# With the `(toffer …)` fix it reuses mtoffstate's instance instead. +import mtoffssz, mtoffgwei, mtoffcodec, mtoffstate +type StateB* = object + field*: HA[64, Gwei] + +proc check*() = + static: doAssert sizeof(StateA) == sizeof(StateB) + echo "ok" diff --git a/tests/ic/mtscopea.nim b/tests/ic/mtscopea.nim new file mode 100644 index 0000000000..985a5da147 --- /dev/null +++ b/tests/ic/mtscopea.nim @@ -0,0 +1,2 @@ +# Helper for ttransitiveoffer.nim: const that the generic body binds at definition. +const TScopeSize* = 65 diff --git a/tests/ic/mtscopeb.nim b/tests/ic/mtscopeb.nim new file mode 100644 index 0000000000..8c119ca511 --- /dev/null +++ b/tests/ic/mtscopeb.nim @@ -0,0 +1,2 @@ +# Helper: a CONFLICTING const of the same name, visible only in the consumer. +const TScopeSize* = 33 diff --git a/tests/ic/mtscopegen.nim b/tests/ic/mtscopegen.nim new file mode 100644 index 0000000000..7e56d25b88 --- /dev/null +++ b/tests/ic/mtscopegen.nim @@ -0,0 +1,7 @@ +# Helper: defines a generic whose body uses TScopeSize via the strformat `&` +# macro (late-bound), resolved in THIS module's scope (mtscopea -> 65). +import mtscopea, std/strformat +export mtscopea +func fromRaw*[T](x: T): string = + const msg = &"size {TScopeSize - 1}" + result = msg & " " & $int(x) diff --git a/tests/ic/mtscopemid.nim b/tests/ic/mtscopemid.nim new file mode 100644 index 0000000000..84ec9ef11a --- /dev/null +++ b/tests/ic/mtscopemid.nim @@ -0,0 +1,3 @@ +# Helper: makes mtscopewarm a TRANSITIVE import of the consumer. +import mtscopewarm +export mtscopewarm diff --git a/tests/ic/mtscopewarm.nim b/tests/ic/mtscopewarm.nim new file mode 100644 index 0000000000..a70c3872e5 --- /dev/null +++ b/tests/ic/mtscopewarm.nim @@ -0,0 +1,4 @@ +# Helper: instantiates fromRaw[int] in a CLEAN scope (no mtscopeb) -> the +# correct instance that the consumer must reuse. +import mtscopegen +proc warm*(): string = fromRaw(5) diff --git a/tests/ic/ttransitiveoffer.nim b/tests/ic/ttransitiveoffer.nim new file mode 100644 index 0000000000..1b14daa08d --- /dev/null +++ b/tests/ic/ttransitiveoffer.nim @@ -0,0 +1,14 @@ +discard """ +output: '''size 64 64''' +""" + +# Regression test for TRANSITIVE generic-instance offers. `fromRaw[int]` is first +# instantiated in mtscopewarm (a clean scope where `TScopeSize` is unambiguously +# 65). The consumer here also imports mtscopeb (`TScopeSize` = 33), so a fresh +# re-instantiation of fromRaw's body would resolve `TScopeSize` ambiguously. The +# clean instance reaches here only TRANSITIVELY (via mtscopemid), so the offer +# rebuild must walk the whole import closure, not just direct imports. Mirrors +# nimbus-eth2 `keys.fromRaw` -> `SkRawPublicKeySize` (secp vs secp256k1). +import mtscopegen, mtscopeb, mtscopemid + +echo fromRaw(64) diff --git a/tests/ic/ttypeoffer.nim b/tests/ic/ttypeoffer.nim new file mode 100644 index 0000000000..bdc25466b1 --- /dev/null +++ b/tests/ic/ttypeoffer.nim @@ -0,0 +1,14 @@ +discard """ +output: '''ok''' +""" + +# Regression test for the `(toffer …)` generic-TYPE-instance sharing across the +# NIF boundary. A `tyGenericInst` whose structure (here an `array` bound) depends +# on a `mixin`/`compiles()` resolved at the instantiation site must be REUSED +# from the module that created it, not re-instantiated in a consumer whose import +# scope flips the `compiles()` and so bakes a different bound. Mirrors the SSZ +# `HashArray[8192, Gwei]` `sizeof` divergence in nimbus-eth2. Before the fix this +# failed under `nim ic` with `doAssert sizeof(StateA) == sizeof(StateB)`. + +import mtoffuser +check()