Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ PublishScripts/
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# and except a Unity project's Packages/, which is source: Unity's package manifest and its
# resolved lock file are both meant to be committed, and a NuGet restore folder never contains
# a file by either name.
!**/[Pp]ackages/manifest.json
!**/[Pp]ackages/packages-lock.json
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
Expand Down Expand Up @@ -651,3 +656,16 @@ Temporary Items

# ImGui.ini files
imgui.ini

# Game engine projects
#
# Godot: the import cache, and the mono/temp bin+obj a C# build writes.
.godot/

# Unity: .meta files are source, not the Visual Studio C++ build artifact that the `*.meta` rule
# further up targets. Unity generates one per asset and it carries the GUID that scenes, prefabs
# and serialized references point at, so ignoring them gives every clone fresh GUIDs and silently
# breaks those references - including for a plug-in whose .dll is itself a build output. This
# negation has to come after that rule to win, and is scoped to the asset tree so the Visual
# Studio artifact stays ignored everywhere else.
!**/[Aa]ssets/**/*.meta
33 changes: 17 additions & 16 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ forms as well, and matching it is a change to every form at once rather than par

### Can the quantities be generated for languages other than C# and C++?

`SevenTargetProjectionTests` is the probe that answers it, and the answer so far is **six of
`SevenTargetProjectionTests` is the probe that answers it, and the answer is now **seven of
seven**. It builds one quantity — the magnitude form of `Length`, read from the real
`dimensions.json` — as a language-agnostic `ktsu.Coder` AST and writes it in all seven of that
library's targets. The neutral shape is the whole of what a generated magnitude is: a record struct
Expand All @@ -260,21 +260,22 @@ C# comes out as exactly what `QuantitiesGenerator` writes today, which is the re
the AST is expressive enough for the quantities, so what the other six do is a question about those
languages rather than about the model.

**One target writes source its own toolchain refuses**, recorded upstream and not fixable here:

| Target | What comes out | Why |
|---|---|---|
| Go | `type Length struct` with no parameters, then `func LengthFromMeter(value T) Length[T]` | ktsu-dev/Coder#63 — a generic type is deliberately written down rather than emitted, and the constructor was not given the same treatment. `go vet` says `undefined: T`. |

The test **pins** it rather than skipping it, so the day it is fixed upstream the test fails and is
updated to assert the fix.

**Python was the second, and the pin is what caught the fix.** It wrote
`class Length(IVector0[Length[T], T])`, which raises `NameError` on import because Python evaluates
a base list eagerly. ktsu-dev/Coder#64 fixed that to the string forward reference
`IVector0["Length[T]", T]`, and the first build after the bump from ktsu.Coder 3.14.0 to 3.14.3
failed here — which is the whole point of pinning rather than skipping, and is how the fix was
noticed at all. The test asserts the fix now, so a regression upstream fails the same way round.
**Two targets used to write source their own toolchain refuses**, both recorded upstream and both
since fixed there. Each was **pinned** rather than skipped, so the day it was fixed the test failed
and was updated to assert the fix — which is how both fixes were noticed at all. The tests assert
the fixes now, so a regression upstream fails the same way round.

**Python was the first to be fixed.** It wrote `class Length(IVector0[Length[T], T])`, which raises
`NameError` on import because Python evaluates a base list eagerly. ktsu-dev/Coder#64 fixed that to
the string forward reference `IVector0["Length[T]", T]`, and the first build after the bump from
ktsu.Coder 3.14.0 to 3.14.3 failed here.

**Go was the second.** A generic type is deliberately written down rather than emitted, and the
constructor was not given the same treatment: it wrote `type Length struct` with no parameters, then
`func LengthFromMeter(value T) Length[T]`, and `go vet` said `undefined: T`. ktsu-dev/Coder#63 wrote
everything over the parameter down too — the field is `Value any` and the constructor is
`func LengthFromMeter(value any) Length` — and the first build after the bump from ktsu.Coder 3.14.3
to 3.18.12 failed here.

The probe lives in `Semantics.Cpp.Test` because that is where the reader of `dimensions.json` is,
and that is itself the finding about this repository: `QuantityMetadata` and `MetadataProjection`
Expand Down
12 changes: 6 additions & 6 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<PackageVersion Include="Polyfill" Version="11.4.3" />
<!-- Test-only: Semantics ships no JSON converter, so the tests register this factory the
same way a consumer is expected to. Not referenced by any shipping project. -->
<PackageVersion Include="ktsu.RoundTripStringJsonConverter" Version="1.0.60" />
<PackageVersion Include="ktsu.PreciseNumber" Version="2.0.3" />
<PackageVersion Include="ktsu.RoundTripStringJsonConverter" Version="1.0.73" />
<PackageVersion Include="ktsu.PreciseNumber" Version="2.6.6" />
<!-- Benchmark-only. -->
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="BenchmarkDotNet.Annotations" Version="0.15.8" />
Expand All @@ -25,11 +25,11 @@
<!-- The C++ projection of the quantity vocabulary. ktsu.Coder publishes net10.0 and net9.0
only, which is why Semantics.Cpp is a project of its own rather than part of a library
that also ships net8.0. -->
<PackageVersion Include="ktsu.Coder" Version="3.14.3" />
<PackageVersion Include="ktsu.Coder" Version="3.18.12" />
<!-- Source generator packages -->
<PackageVersion Include="ktsu.CodeBlocker" Version="2.1.2" />
<PackageVersion Include="ktsu.SourceGeneratorToolkit" Version="1.0.5" />
<PackageVersion Include="ktsu.SourceGeneratorToolkit.Testing" Version="1.0.5" />
<PackageVersion Include="ktsu.CodeBlocker" Version="2.3.2" />
<PackageVersion Include="ktsu.SourceGeneratorToolkit" Version="1.2.5" />
<PackageVersion Include="ktsu.SourceGeneratorToolkit.Testing" Version="1.2.5" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.9.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="5.9.0" />
<!-- Build-time only: the generator reads its metadata with it. No shipping project
Expand Down
32 changes: 18 additions & 14 deletions Semantics.Cpp.Test/SevenTargetProjectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,10 @@ namespace ktsu.Semantics.Cpp.Test;
/// before a neutral projection could be a project of its own.
/// </para>
/// <para>
/// <b>One target is still known to produce source its own toolchain refuses</b>, and the test
/// below pins that rather than skipping it, so the day it is fixed upstream the test fails and
/// says so. It is recorded against ktsu.Coder as ktsu-dev/Coder#63 and is not anything this
/// repository can fix. Python was the second such target until ktsu-dev/Coder#64 was fixed; its
/// pin failed exactly as intended, and it now asserts the fix.
/// <b>Two targets used to produce source their own toolchains refuse</b>, and each was pinned
/// rather than skipped, so the day it was fixed upstream its test failed and said so. Python was
/// fixed by ktsu-dev/Coder#64 and Go by ktsu-dev/Coder#63; both pins failed exactly as intended,
/// and both now assert the fix, so a regression upstream fails here the same way round.
/// </para>
/// </remarks>
[TestClass]
Expand Down Expand Up @@ -120,26 +119,31 @@ public void TheTargetsWithoutTypeParametersWriteItDown()
}

/// <summary>
/// Go writes source the Go toolchain refuses, because it drops the type parameter from the type
/// and then spells the type as generic anyway.
/// Go writes the generic type down, and everything written over it with it, so nothing names a
/// type parameter the type never declared.
/// </summary>
/// <remarks>
/// A generic type is deliberately written down rather than emitted, because a method on one
/// needs the parameters in three places and spelled two ways. The constructor was not given the
/// same treatment, so it returns <c>Length[T]</c> from a <c>Length</c> that takes no parameters,
/// and the field's type <c>T</c> is undefined. `go vet` says `undefined: T`.
/// needs the parameters in three places and spelled two ways. The constructor used not to be
/// given the same treatment: it returned <c>Length[T]</c> from a <c>Length</c> that takes no
/// parameters, and its parameter's type <c>T</c> was undefined, which `go vet` reported as
/// `undefined: T`. ktsu.Coder now types a member written over a parameter as <c>any</c> and
/// mentions the type without arguments, so the declaration agrees with itself.
/// <para>
/// Pinned rather than skipped: this asserts the inconsistency exists, so fixing it upstream
/// fails here and this test is updated to assert the fix instead. ktsu-dev/Coder#63.
/// This was pinned as a defect until ktsu-dev/Coder#63 was fixed, which the bump from
/// ktsu.Coder 3.14.3 to 3.18.12 brought in. The pin failed, as it was written to, and asserts
/// the fix instead.
/// </para>
/// </remarks>
[TestMethod]
public void GoSpellsAGenericTypeItDidNotDeclare()
public void GoWritesTheGenericTypeDownAndEverythingOverIt()
{
string source = Written["go"];

Assert.Contains("type Length struct", source, "the type is written without parameters");
Assert.Contains("Length[T]", source, "and the constructor spells it with one anyway");
Assert.Contains("Value any", source, "the field over the parameter is typed any");
Assert.Contains("func LengthFromMeter(value any) Length {", source, "and so is the constructor");
Assert.DoesNotContain("[T]", source, "nothing spells the type with a parameter it does not declare");
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions Semantics.Cpp/Semantics.Cpp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="ktsu.CodeBlocker" VersionOverride="2.3.2" />
<PackageReference Include="ktsu.Coder" />
<PackageReference Include="Polyfill" PrivateAssets="all" />
</ItemGroup>
Expand Down
18 changes: 9 additions & 9 deletions global.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
},
"msbuild-sdks": {
"MSTest.Sdk": "4.4.0",
"ktsu.Sdk": "2.28.1",
"ktsu.Sdk.ConsoleApp": "2.28.1",
"ktsu.Sdk.Tool": "2.28.1",
"ktsu.Sdk.App": "2.28.1",
"ktsu.Sdk.Windows": "2.28.1",
"ktsu.Sdk.Linux": "2.28.1",
"ktsu.Sdk.macOS": "2.28.1",
"ktsu.Sdk.iOS": "2.28.1",
"ktsu.Sdk.Android": "2.28.1"
"ktsu.Sdk": "2.31.2",
"ktsu.Sdk.ConsoleApp": "2.31.2",
"ktsu.Sdk.Tool": "2.31.2",
"ktsu.Sdk.App": "2.31.2",
"ktsu.Sdk.Windows": "2.31.2",
"ktsu.Sdk.Linux": "2.31.2",
"ktsu.Sdk.macOS": "2.31.2",
"ktsu.Sdk.iOS": "2.31.2",
"ktsu.Sdk.Android": "2.31.2"
},
"test": {
"runner": "Microsoft.Testing.Platform"
Expand Down
Loading