mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-14 11:48:28 -04:00
f9ccf4c1c4
# Description of Changes This PR fixes a C# SDK regression where using `Bound` in index filters could trigger an ambiguous reference compiler error for Local after upgrading to `v1.11.2`, as reported in [#3995](https://github.com/clockworklabs/SpacetimeDB/issues/3995). It also fixes a related warning-spam regression (`CS0436`) where user projects could see `Local` type conflicts between generated module code and the `SpacetimeDB.Runtime` assembly. * Introduced a public `SpacetimeDB.Bound` type so users no longer need to import `SpacetimeDB.Internal` to use bounds in index filters. * Kept `SpacetimeDB.Internal.Bound` for compatibility, but added implicit conversions between `SpacetimeDB.Internal.Bound` and `SpacetimeDB.Bound`. * Updated the C# code generator to emit fully-qualified `global::SpacetimeDB.Bound` in generated index filter signatures, avoiding `SpacetimeDB.Internal` in public-facing APIs and preventing name collisions (e.g., `Local`). * Updated internal runtime bounds helpers (`BTreeIndexBounds<...>`) to explicitly use `SpacetimeDB.Bound` when constructing range-scan arguments. * Updated Codegen snapshot fixtures to match the new generated output (type name + formatting). * Fixed codegen output for `ITableView` `static abstract` member implementations to generate `public static` methods (required for the generated code to compile). It also fixes a related warning-spam regression (CS0436) where user projects could see Local type conflicts between generated module code and the SpacetimeDB.Runtime assembly. Additional fix (related to the `Local` reports): * Removed the runtime assembly’s ownership of `SpacetimeDB.Local` (introduced more recently than the generated module `Local`) to prevent `CS0436` duplicate-type warnings. Basically, the runtime’s concrete `Local`/`ProcedureTxContext` helpers were renamed and made internal so the code generator remains the sole owner of module-level `SpacetimeDB.Local`. Regression coverage: * Added generator regression assertions to ensure generated code does not reference `global::SpacetimeDB.Internal.Bound<...>` and does reference `global::SpacetimeDB.Bound<...>`. * Added a runtime API regression assertion that `SpacetimeDB.Bound` exists and is public in the runtime reference. * Added a regression assertion that `SpacetimeDB.Runtime` does not define codegen-owned types (e.g. `SpacetimeDB.Local`, `ProcedureContext`, etc.) to prevent future `CS0436` conflicts. * Added a “simulated downstream user file” compile check ensuring no `CS0436` diagnostics occur when user code references `SpacetimeDB.Local`. # API and ABI breaking changes None. * No schema or wire-format changes. * The changes are limited to C# type exposure / naming and codegen output. * `SpacetimeDB.Internal.Bound` remains usable via implicit conversions (backwards compatible for existing code). # Expected complexity level and risk 2 - Low * Changes are isolated to C# runtime type exposure, codegen type references, and snapshot updates. * No runtime behavior changes to index scan encoding/decoding; only avoids requiring SpacetimeDB.Internal in user code. # Testing - [X] Ran:`dotnet test crates/bindings-csharp/Codegen.Tests/Codegen.Tests.csproj` - [X] Ran regression tests locally.
426 lines
11 KiB
C#
426 lines
11 KiB
C#
using System.IO;
|
|
using SpacetimeDB.BSATN;
|
|
|
|
namespace SpacetimeDB
|
|
{
|
|
public readonly struct Bound<T>(T min, T max)
|
|
{
|
|
public T Min => min;
|
|
public T Max => max;
|
|
|
|
public static implicit operator Bound<T>(T value) => new(value, value);
|
|
|
|
public static implicit operator Bound<T>((T min, T max) span) => new(span.min, span.max);
|
|
}
|
|
}
|
|
|
|
namespace SpacetimeDB.Internal
|
|
{
|
|
enum BoundVariant : byte
|
|
{
|
|
Inclusive,
|
|
Exclusive,
|
|
Unbounded,
|
|
}
|
|
|
|
public interface IBTreeIndexBounds
|
|
{
|
|
ushort PrefixElems { get; }
|
|
void Prefix(BinaryWriter w);
|
|
void RStart(BinaryWriter w);
|
|
void REnd(BinaryWriter w);
|
|
}
|
|
|
|
public readonly struct Bound<T>(T min, T max)
|
|
{
|
|
public T Min => min;
|
|
public T Max => max;
|
|
|
|
public static implicit operator Bound<T>(T value) => new(value, value);
|
|
|
|
public static implicit operator Bound<T>((T min, T max) span) => new(span.min, span.max);
|
|
|
|
public static implicit operator SpacetimeDB.Bound<T>(Bound<T> value) =>
|
|
new(value.Min, value.Max);
|
|
|
|
public static implicit operator Bound<T>(SpacetimeDB.Bound<T> value) =>
|
|
new(value.Min, value.Max);
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<T, TRW>(SpacetimeDB.Bound<T> t) : IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
{
|
|
public ushort PrefixElems => 0;
|
|
|
|
public void Prefix(BinaryWriter _) { }
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new TRW().Write(w, t.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new TRW().Write(w, t.Max);
|
|
}
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<T, TRW, U, URW>((T t, SpacetimeDB.Bound<U> u) b)
|
|
: IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
where URW : struct, IReadWrite<U>
|
|
{
|
|
public ushort PrefixElems => 1;
|
|
|
|
public void Prefix(BinaryWriter w)
|
|
{
|
|
new TRW().Write(w, b.t);
|
|
}
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new URW().Write(w, b.u.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new URW().Write(w, b.u.Max);
|
|
}
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW>(
|
|
(T t, U u, SpacetimeDB.Bound<V> v) b
|
|
) : IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
where URW : struct, IReadWrite<U>
|
|
where VRW : struct, IReadWrite<V>
|
|
{
|
|
public ushort PrefixElems => 2;
|
|
|
|
public void Prefix(BinaryWriter w)
|
|
{
|
|
new TRW().Write(w, b.t);
|
|
new URW().Write(w, b.u);
|
|
}
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new VRW().Write(w, b.v.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new VRW().Write(w, b.v.Max);
|
|
}
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW, W, WRW>(
|
|
(T t, U u, V v, SpacetimeDB.Bound<W> w) b
|
|
) : IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
where URW : struct, IReadWrite<U>
|
|
where VRW : struct, IReadWrite<V>
|
|
where WRW : struct, IReadWrite<W>
|
|
{
|
|
public ushort PrefixElems => 3;
|
|
|
|
public void Prefix(BinaryWriter w)
|
|
{
|
|
new TRW().Write(w, b.t);
|
|
new URW().Write(w, b.u);
|
|
new VRW().Write(w, b.v);
|
|
}
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new WRW().Write(w, b.w.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new WRW().Write(w, b.w.Max);
|
|
}
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW, W, WRW, X, XRW>(
|
|
(T t, U u, V v, W w, SpacetimeDB.Bound<X> x) b
|
|
) : IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
where URW : struct, IReadWrite<U>
|
|
where VRW : struct, IReadWrite<V>
|
|
where WRW : struct, IReadWrite<W>
|
|
where XRW : struct, IReadWrite<X>
|
|
{
|
|
public ushort PrefixElems => 4;
|
|
|
|
public void Prefix(BinaryWriter w)
|
|
{
|
|
new TRW().Write(w, b.t);
|
|
new URW().Write(w, b.u);
|
|
new VRW().Write(w, b.v);
|
|
new WRW().Write(w, b.w);
|
|
}
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new XRW().Write(w, b.x.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new XRW().Write(w, b.x.Max);
|
|
}
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW, W, WRW, X, XRW, Y, YRW>(
|
|
(T t, U u, V v, W w, X x, SpacetimeDB.Bound<Y> y) b
|
|
) : IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
where URW : struct, IReadWrite<U>
|
|
where VRW : struct, IReadWrite<V>
|
|
where WRW : struct, IReadWrite<W>
|
|
where XRW : struct, IReadWrite<X>
|
|
where YRW : struct, IReadWrite<Y>
|
|
{
|
|
public ushort PrefixElems => 5;
|
|
|
|
public void Prefix(BinaryWriter w)
|
|
{
|
|
new TRW().Write(w, b.t);
|
|
new URW().Write(w, b.u);
|
|
new VRW().Write(w, b.v);
|
|
new WRW().Write(w, b.w);
|
|
new XRW().Write(w, b.x);
|
|
}
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new YRW().Write(w, b.y.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new YRW().Write(w, b.y.Max);
|
|
}
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW, W, WRW, X, XRW, Y, YRW, Z, ZRW>(
|
|
(T t, U u, V v, W w, X x, Y y, SpacetimeDB.Bound<Z> z) b
|
|
) : IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
where URW : struct, IReadWrite<U>
|
|
where VRW : struct, IReadWrite<V>
|
|
where WRW : struct, IReadWrite<W>
|
|
where XRW : struct, IReadWrite<X>
|
|
where YRW : struct, IReadWrite<Y>
|
|
where ZRW : struct, IReadWrite<Z>
|
|
{
|
|
public ushort PrefixElems => 6;
|
|
|
|
public void Prefix(BinaryWriter w)
|
|
{
|
|
new TRW().Write(w, b.t);
|
|
new URW().Write(w, b.u);
|
|
new VRW().Write(w, b.v);
|
|
new WRW().Write(w, b.w);
|
|
new XRW().Write(w, b.x);
|
|
new YRW().Write(w, b.y);
|
|
}
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new ZRW().Write(w, b.z.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new ZRW().Write(w, b.z.Max);
|
|
}
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<
|
|
T,
|
|
TRW,
|
|
U,
|
|
URW,
|
|
V,
|
|
VRW,
|
|
W,
|
|
WRW,
|
|
X,
|
|
XRW,
|
|
Y,
|
|
YRW,
|
|
Z,
|
|
ZRW,
|
|
A,
|
|
ARW
|
|
>((T t, U u, V v, W w, X x, Y y, Z z, SpacetimeDB.Bound<A> a) b) : IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
where URW : struct, IReadWrite<U>
|
|
where VRW : struct, IReadWrite<V>
|
|
where WRW : struct, IReadWrite<W>
|
|
where XRW : struct, IReadWrite<X>
|
|
where YRW : struct, IReadWrite<Y>
|
|
where ZRW : struct, IReadWrite<Z>
|
|
where ARW : struct, IReadWrite<A>
|
|
{
|
|
public ushort PrefixElems => 7;
|
|
|
|
public void Prefix(BinaryWriter w)
|
|
{
|
|
new TRW().Write(w, b.t);
|
|
new URW().Write(w, b.u);
|
|
new VRW().Write(w, b.v);
|
|
new WRW().Write(w, b.w);
|
|
new XRW().Write(w, b.x);
|
|
new YRW().Write(w, b.y);
|
|
new ZRW().Write(w, b.z);
|
|
}
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new ARW().Write(w, b.a.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new ARW().Write(w, b.a.Max);
|
|
}
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<
|
|
T,
|
|
TRW,
|
|
U,
|
|
URW,
|
|
V,
|
|
VRW,
|
|
W,
|
|
WRW,
|
|
X,
|
|
XRW,
|
|
Y,
|
|
YRW,
|
|
Z,
|
|
ZRW,
|
|
A,
|
|
ARW,
|
|
B,
|
|
BRW
|
|
>((T t, U u, V v, W w, X x, Y y, Z z, A a, SpacetimeDB.Bound<B> b) b) : IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
where URW : struct, IReadWrite<U>
|
|
where VRW : struct, IReadWrite<V>
|
|
where WRW : struct, IReadWrite<W>
|
|
where XRW : struct, IReadWrite<X>
|
|
where YRW : struct, IReadWrite<Y>
|
|
where ZRW : struct, IReadWrite<Z>
|
|
where ARW : struct, IReadWrite<A>
|
|
where BRW : struct, IReadWrite<B>
|
|
{
|
|
public ushort PrefixElems => 8;
|
|
|
|
public void Prefix(BinaryWriter w)
|
|
{
|
|
new TRW().Write(w, b.t);
|
|
new URW().Write(w, b.u);
|
|
new VRW().Write(w, b.v);
|
|
new WRW().Write(w, b.w);
|
|
new XRW().Write(w, b.x);
|
|
new YRW().Write(w, b.y);
|
|
new ZRW().Write(w, b.z);
|
|
new ARW().Write(w, b.a);
|
|
}
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new BRW().Write(w, b.b.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new BRW().Write(w, b.b.Max);
|
|
}
|
|
}
|
|
|
|
public readonly struct BTreeIndexBounds<
|
|
T,
|
|
TRW,
|
|
U,
|
|
URW,
|
|
V,
|
|
VRW,
|
|
W,
|
|
WRW,
|
|
X,
|
|
XRW,
|
|
Y,
|
|
YRW,
|
|
Z,
|
|
ZRW,
|
|
A,
|
|
ARW,
|
|
B,
|
|
BRW,
|
|
C,
|
|
CRW
|
|
>((T t, U u, V v, W w, X x, Y y, Z z, A a, B b, SpacetimeDB.Bound<C> c) b) : IBTreeIndexBounds
|
|
where TRW : struct, IReadWrite<T>
|
|
where URW : struct, IReadWrite<U>
|
|
where VRW : struct, IReadWrite<V>
|
|
where WRW : struct, IReadWrite<W>
|
|
where XRW : struct, IReadWrite<X>
|
|
where YRW : struct, IReadWrite<Y>
|
|
where ZRW : struct, IReadWrite<Z>
|
|
where ARW : struct, IReadWrite<A>
|
|
where BRW : struct, IReadWrite<B>
|
|
where CRW : struct, IReadWrite<C>
|
|
{
|
|
public ushort PrefixElems => 9;
|
|
|
|
public void Prefix(BinaryWriter w)
|
|
{
|
|
new TRW().Write(w, b.t);
|
|
new URW().Write(w, b.u);
|
|
new VRW().Write(w, b.v);
|
|
new WRW().Write(w, b.w);
|
|
new XRW().Write(w, b.x);
|
|
new YRW().Write(w, b.y);
|
|
new ZRW().Write(w, b.z);
|
|
new ARW().Write(w, b.a);
|
|
new BRW().Write(w, b.b);
|
|
}
|
|
|
|
public void RStart(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new CRW().Write(w, b.c.Min);
|
|
}
|
|
|
|
public void REnd(BinaryWriter w)
|
|
{
|
|
w.Write((byte)BoundVariant.Inclusive);
|
|
new CRW().Write(w, b.c.Max);
|
|
}
|
|
}
|
|
}
|