Skip to content

The C++ and .NET generators read physicalConstraints by different rules, so the next constraint added will fork the two APIs #218

Description

@matt-edmondson

What happens

The same metadata field decides a runtime guard, and the two projections test it differently.

.NET — exact value match (Semantics.SourceGenerators/Generators/QuantitiesGenerator.cs:1372-1373):

bool strictPositive = vectorForm == 0
    && overload.PhysicalConstraints?.MinExclusive == "0";

C++ — presence only (Semantics.Cpp/QuantityVocabulary.cs:155):

overload.PhysicalConstraints is null ? Magnitude.NonNegative : Magnitude.Positive));

MetadataConstraints.MinExclusive defaults to string.Empty (Semantics.Cpp/QuantityMetadata.cs:130), so a non-null constraints object with no minExclusive — or with any value other than "0" — lands on opposite answers.

Failure scenario

Add "physicalConstraints": {}, or {"minExclusive": "1"}, to any V0 overload.

C++ emits the strict guard (Semantics.Cpp/CppQuantityGenerator.cs:239-244):

string comparison = type.Magnitude == Magnitude.Positive ? ">" : ">=";
...
return $"(assert({ValueName}.count() {comparison} 0 && \"{says}\"), {ValueName})";

assert(value > 0), rejecting zero.

.NET keeps EnsureNonNegative, accepting zero.

The same declared quantity now has two different domains depending on which language you generate.

Why it matters

Today's three constraint sites — Wavelength, Period, HalfLife, all {"minExclusive": "0"} — agree by coincidence. So nothing is broken right now, and nothing would catch it breaking.

CLAUDE.md's design decision #4 is explicit that the strict rule is opted into by minExclusive: "0" specifically:

A V0 overload can opt into a stricter rule by declaring physicalConstraints: { "minExclusive": "0" } in dimensions.json (#51); the generator then emits Vector0Guards.EnsurePositive and rejects zero too.

The C++ side does not implement that rule — it implements "any constraints at all". The next constraint added to the metadata silently forks the two APIs, and the divergence surfaces as a C++ assertion failing on a value .NET accepts.

Suggested fix

Make QuantityVocabulary test PhysicalConstraints?.MinExclusive == "0", matching QuantitiesGenerator.

Better still, factor the predicate so both generators call one implementation — this is the second case in this repo (see the dimensional-check divergence) where one metadata file is read by two generators with no shared decision logic.

Add a Semantics.Cpp.Test case asserting the Magnitude chosen for an overload with a non-"0" constraint, so the two cannot drift again.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions