From fa1527e84360bef9d78c21ff92b7bc9d313cc2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Gro=C3=9F?= Date: Sat, 31 Oct 2026 20:58:20 +0100 Subject: [PATCH 1/2] spike: Metalama 2026.1 on the .NET 11 RC3 SDK Repeats decision 120's Metalama measurement on SDK 11.0.100-rc.3.26522.148 with the published 2.3.1 packages: every RC1 result holds. The compiler is 2026.1.20 at C# 14, a declared union fails (CS0106, CS8803, CS1001), a match on a library's union fails (CS8652), and Metalama.Framework 2027.0.5-preview runs C# 15 and builds both. A web project on 2026.1 fails with LAMA0625 when SDK 11 is the only SDK in its installation, on RC1 as on RC3 and for net10.0 too; with SDK 10 beside it, it builds with LAMA0617 as decision 120 says. Filed as #275. KB-060 records the fact. Refs #170 Co-Authored-By: Claude Opus 5.5 --- ...B-060-metalama-2026-1-on-the-net-11-sdk.md | 37 +++++ design/knowledge/README.md | 1 + spikes/Sdk11Rc3Metalama/App/App.csproj | 35 +++++ spikes/Sdk11Rc3Metalama/App/Program.cs | 54 +++++++ spikes/Sdk11Rc3Metalama/Directory.Build.props | 3 + spikes/Sdk11Rc3Metalama/README.md | 134 ++++++++++++++++++ spikes/Sdk11Rc3Metalama/UnionLib/Pets.cs | 12 ++ .../Sdk11Rc3Metalama/UnionLib/UnionLib.csproj | 7 + spikes/Sdk11Rc3Metalama/WebApp/Program.cs | 7 + spikes/Sdk11Rc3Metalama/WebApp/WebApp.csproj | 21 +++ spikes/Sdk11Rc3Metalama/global.json | 11 ++ spikes/Sdk11Rc3Metalama/nuget.config | 8 ++ spikes/Sdk11Rc3Metalama/run.sh | 42 ++++++ spikes/Sdk11Rc3Metalama/web-matrix.sh | 55 +++++++ 14 files changed, 427 insertions(+) create mode 100644 design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md create mode 100644 spikes/Sdk11Rc3Metalama/App/App.csproj create mode 100644 spikes/Sdk11Rc3Metalama/App/Program.cs create mode 100644 spikes/Sdk11Rc3Metalama/Directory.Build.props create mode 100644 spikes/Sdk11Rc3Metalama/README.md create mode 100644 spikes/Sdk11Rc3Metalama/UnionLib/Pets.cs create mode 100644 spikes/Sdk11Rc3Metalama/UnionLib/UnionLib.csproj create mode 100644 spikes/Sdk11Rc3Metalama/WebApp/Program.cs create mode 100644 spikes/Sdk11Rc3Metalama/WebApp/WebApp.csproj create mode 100644 spikes/Sdk11Rc3Metalama/global.json create mode 100644 spikes/Sdk11Rc3Metalama/nuget.config create mode 100755 spikes/Sdk11Rc3Metalama/run.sh create mode 100755 spikes/Sdk11Rc3Metalama/web-matrix.sh diff --git a/design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md b/design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md new file mode 100644 index 00000000..2b4a3f9a --- /dev/null +++ b/design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md @@ -0,0 +1,37 @@ +--- +verified: 2026-10-31 +pins: + - external .NET-SDK 11.0.100-rc.3.26522.148 + - external Metalama.Framework 2026.1.30 + - external Metalama.Framework 2027.0.5-preview +source: spikes/Sdk11Rc3Metalama, measured 2026-10-31 on macOS arm64 with the published CodoMetis.TypeKit and CodoMetis.TypeKit.Generators 2.3.1, against SDK 11.0.100-rc.3.26522.148 and 11.0.100-rc.1.26425.128, each alone in its .NET installation, and RC3 with SDK 10.0.403 beside it +turned-on: decision 120 (a net11.0 target in 2.4.0 on Metalama 2026.1, and Metalama 2027.0 in 2.5.0), and the RC3 part of issue 170 +test: none, a measurement of SDKs and compilers that CI does not install; the spike's run.sh and web-matrix.sh repeat it +--- + +# KB-060. Metalama 2026.1 weaves a `net11.0` project on SDK 11 RC3 at C# 14, and a web project fails when SDK 11 is the only SDK + +On SDK 11.0.100-rc.3, with the 2.3.1 packages (`Metalama.Framework` 2026.1.30, `Metalama.Compiler` 2026.1.20): + +- A `net11.0` console project with a value object builds, weaves and runs: `From`, `Parse` and a JSON round trip. + No warning. +- `#error version` names Metalama's compiler 2026.1.20 at language version 14.0. `Metalama.Framework` 2026.1.31 + (compiler 2026.1.21) is at 14.0 too. +- A `union` declared in the project fails with `CS0106`, `CS1001` and `CS8803`; a `switch` over a union returned by + a `net11.0`-only library fails with `CS8652` ("unions ... in Preview"); holding that union builds. Without Metalama + both compile. +- A direct `Metalama.Framework` 2027.0.5-preview reference beside the 2.3.1 packages: compiler 2027.0.5-preview at + 15.0, the value object woven, and both union cases build and run. + +These are the RC1 results of decision 120, unchanged. + +**A web project (`Microsoft.NET.Sdk.Web`) on Metalama 2026.1 fails with `LAMA0625` when SDK 11 is the only SDK in +its .NET installation**, on RC1 and on RC3, with 2026.1.31, and for a `net10.0` project as for a `net11.0` one. The SDK 11 Razor compiler needs Roslyn 5.11 (RC1) or +5.12 (RC3), Metalama 2026.1 bundles 5.9, and Metalama finds no older SDK in that installation to take a compatible +Razor compiler from ("Installed SDKs found: 11.0.100"). With SDK 10.0.403 in the same installation the build passes +with two `LAMA0617` warnings (the ASP.NET analyzers from SDK 10.0.401, the Razor compiler from SDK 10.0.403). +Metalama 2027.0.5-preview builds the web project with neither. Pinned to SDK 10.0.403, `net10.0`, both versions build +it with no warning. + +Not measured: the general availability SDK, Linux and Windows, an IDE, a Razor Pages or Blazor project, and any +2027 build without a Metalama license on the machine (this machine has a license entry for 2027.0). diff --git a/design/knowledge/README.md b/design/knowledge/README.md index e1f22505..d0171648 100644 --- a/design/knowledge/README.md +++ b/design/knowledge/README.md @@ -88,3 +88,4 @@ test and later for the harness's `Baseline.Fact`. | [KB-055](KB-055-ericksonlopez-domainprimitives.md) | EricksonLopez.DomainPrimitives 3.0.0 is an attribute on a partial record struct, with a catalogue of ready-made types, re-validated on every read | EricksonLopez.DomainPrimitives 3.0.0 | prose | | [KB-057](KB-057-a-nullable-contract-has-an-element-type-and-reads-null-as-null.md) | A `Nullable` contract has `T` as its element type, and a JSON null read into it is null whatever `T`'s converter does | OpenApi 10.0.12, SDK 10.0.4xx | `KB057` | | [KB-058](KB-058-ef-orders-captured-values-in-sql-only-with-a-lambda.md) | EF orders captured values in SQL only when the chain takes a lambda: `values.Max()` runs in memory, `values.Max(v => v)` in SQL | EF Sqlite 10.0.12 | `KB058` | +| [KB-060](KB-060-metalama-2026-1-on-the-net-11-sdk.md) | Metalama 2026.1 weaves a `net11.0` project on SDK 11 RC3 at C# 14, and a web project fails with `LAMA0625` when SDK 11 is the only SDK | .NET SDK 11 RC3, Metalama 2026.1.30, 2027.0.5-preview | prose | diff --git a/spikes/Sdk11Rc3Metalama/App/App.csproj b/spikes/Sdk11Rc3Metalama/App/App.csproj new file mode 100644 index 00000000..db6ab958 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/App/App.csproj @@ -0,0 +1,35 @@ + + + + Exe + net11.0 + enable + enable + false + + + $(DefineConstants);PROBE_VERSION + $(DefineConstants);DECLARE_UNION + $(DefineConstants);HOLD_UNION + $(DefineConstants);MATCH_UNION + $(DefineConstants);NO_GENERATORS + + + + + + + + + + + + diff --git a/spikes/Sdk11Rc3Metalama/App/Program.cs b/spikes/Sdk11Rc3Metalama/App/Program.cs new file mode 100644 index 00000000..841528d4 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/App/Program.cs @@ -0,0 +1,54 @@ +#if PROBE_VERSION +#error version +#endif + +#if !NO_GENERATORS +using System.Text.Json; +using CodoMetis.TypeKit.ValueObjects; + +var id = OrderId.From(Guid.Parse("0b9f7d3a-5c1e-4f5a-9d8e-2a7c6b4e1f03")); +var parsed = OrderId.Parse("0b9f7d3a-5c1e-4f5a-9d8e-2a7c6b4e1f03", null); +var json = JsonSerializer.Serialize(id); +var read = JsonSerializer.Deserialize(json); +Console.WriteLine($"From: {id}"); +Console.WriteLine($"Parse equal: {parsed == id}"); +Console.WriteLine($"JSON: {json}, read back equal: {read == id}"); +#endif + +#if DECLARE_UNION +Shape shape = new Circle(1.5); +Console.WriteLine(shape switch +{ + Circle c => $"declared union: circle {c.Radius}", + Square s => $"declared union: square {s.Side}", +}); +#endif + +#if HOLD_UNION +var held = UnionLib.Pets.Adopt(); +Console.WriteLine($"held: {held}"); +#endif + +#if MATCH_UNION +Console.WriteLine(UnionLib.Pets.Adopt() switch +{ + UnionLib.Cat c => $"matched union: cat {c.Name}", + UnionLib.Dog d => $"matched union: dog {d.Name}", +}); +#endif + +#if NO_GENERATORS +Console.WriteLine("control: no TypeKit, no Metalama"); +#endif + +#if !NO_GENERATORS +public readonly partial record struct OrderId : IValue; +#endif + +#if DECLARE_UNION +public sealed record Circle(double Radius); + +public sealed record Square(double Side); + +public union Shape(Circle, Square); +#endif diff --git a/spikes/Sdk11Rc3Metalama/Directory.Build.props b/spikes/Sdk11Rc3Metalama/Directory.Build.props new file mode 100644 index 00000000..578e79b1 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/Directory.Build.props @@ -0,0 +1,3 @@ + + + diff --git a/spikes/Sdk11Rc3Metalama/README.md b/spikes/Sdk11Rc3Metalama/README.md new file mode 100644 index 00000000..9306e273 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/README.md @@ -0,0 +1,134 @@ +# Spike: does Metalama 2026.1 still hold on the .NET 11 RC3 SDK? + +**Status: run 2026-10-31, answered.** The RC3 part of [#170](https://github.com/CaffeinatedCoder/CodoMetis.TypeKit/issues/170): +[decision 120](../../design/adr/0120-a-net11-0-target-and-documented-floor-raises-join-a-minor.md)'s Metalama +measurement, made on SDK 11.0.100-rc.1 on 2026-10-29, repeated on release candidate 3. Recorded as +[KB-060](../../design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md). + +**Question.** With the published 2.3.1 packages (Metalama 2026.1) in a `net11.0` project on SDK 11.0.100-rc.3: + +1. does a value object with no union build, weave and run (`From`, `Parse`, JSON)? +2. which compiler, at which language version, does `#error version` name? +3. what does a `union` declared in the project give? +4. what does a `switch` over a union returned by a `net11.0`-only library give? +5. is LAMA0617 still reported, and is anything new? +6. what changes with a direct `Metalama.Framework` 2027 reference beside the 2.3.1 packages? + +**Answer: every RC1 result holds on RC3.** Metalama 2026.1 builds, weaves and runs a `net11.0` value object, its +compiler (2026.1.20) runs the project at C# 14, a declared union fails with `CS0106`, `CS8803` and `CS1001`, and a +`switch` over a library's union with `CS8652`. With `Metalama.Framework` 2027.0.5-preview the compiler is 2027 at +C# 15 and both union rows build and run. The latest 2026.1 patch (2026.1.31, compiler 2026.1.21) is also at C# 14. + +**One finding that the RC1 runs could not show, and that is not an RC3 change.** A web project +(`Microsoft.NET.Sdk.Web`, which loads the Razor source generator) on Metalama 2026.1 **fails** with `LAMA0625` +when SDK 11 is the only SDK in its .NET installation, on RC1 as on RC3, with 2026.1.31 too, and whether the +project targets `net11.0` or `net10.0`: a 2.x web application that moves its build to SDK 11 alone stops building. Metalama's compiler +bundles Roslyn 5.9; the SDK 11 Razor compiler needs 5.11 (RC1) or 5.12 (RC3), and Metalama looks for an older SDK to +take a compatible Razor compiler from. With SDK 10.0.403 installed beside it, that search succeeds and the build passes +with the `LAMA0617` warning decision 120 describes. Decision 120's runs had SDK 10.0.403 beside RC1, so they saw only +the warning. A console project reports neither. Metalama 2027.0.5-preview builds the web project in every cell. +Filed as [#275](https://github.com/CaffeinatedCoder/CodoMetis.TypeKit/issues/275). + +## The projects + +- `UnionLib`: a `net11.0` library with no Metalama that declares `public union Pet(Cat, Dog)` and returns one. +- `App`: a `net11.0` console project on `CodoMetis.TypeKit` and `CodoMetis.TypeKit.Generators` 2.3.1, with + `OrderId : IValue`. Properties switch the rows (`App.csproj` lists them): `ProbeVersion`, `DeclareUnion`, + `HoldUnion`, `MatchUnion`, `MetalamaFramework=` (a direct reference) and `NoGenerators` (the control). +- `WebApp`: the same value object as a minimal-API route parameter, in `Microsoft.NET.Sdk.Web`. It suppresses + ASP0020, the documented false alarm on a route parameter + ([decision 12](../../design/adr/0012-asp0020-documented-suppressed.md)), which fires on SDK 10 as on SDK 11. + +`nuget.config` clears every source but nuget.org, and the runs restore into an empty `NUGET_PACKAGES` folder, so +the 2.3.1 packages are the published ones. Every build runs with `UseSharedCompilation=false` and +`-nodeReuse:false`, after `dotnet build-server shutdown` and with no compiler process left. + +## Running it + +The SDKs are installed side by side with Microsoft's script, leaving `/usr/local/share/dotnet` as it is: + +```bash +./dotnet-install.sh --version 11.0.100-rc.3.26522.148 --install-dir ~/.dotnet-sdk11-rc3 --no-path +./dotnet-install.sh --version 11.0.100-rc.3.26522.148 --install-dir ~/.dotnet-sdk11-rc3-sdk10 --no-path +./dotnet-install.sh --version 10.0.403 --install-dir ~/.dotnet-sdk11-rc3-sdk10 --no-path +./dotnet-install.sh --version 11.0.100-rc.1.26425.128 --install-dir ~/.dotnet-sdk11-rc1 --no-path +``` + +```bash +DOTNET=~/.dotnet-sdk11-rc3/dotnet NUGET_PACKAGES= spikes/Sdk11Rc3Metalama/run.sh +RC3_ALONE=~/.dotnet-sdk11-rc3/dotnet RC3_BESIDE=~/.dotnet-sdk11-rc3-sdk10/dotnet RC1_ALONE=~/.dotnet-sdk11-rc1/dotnet \ + NUGET_PACKAGES= spikes/Sdk11Rc3Metalama/web-matrix.sh +``` + +`run.sh` builds `App` once per row and runs what builds; `web-matrix.sh` builds `WebApp` under each installation, +the SDK 10, RC1 and `net10.0` cells from a copy whose `global.json` pins that SDK. Each writes a log per row and `summary.txt`. + +## Evidence + +SDK 11.0.100-rc.3.26522.148 (runtime 11.0.0-rc.3.26522.148), macOS 27.0.1 on arm64. `CodoMetis.TypeKit` and +`CodoMetis.TypeKit.Generators` 2.3.1, which restore `Metalama.Framework` 2026.1.30 and `Metalama.Compiler` +2026.1.20; `Metalama.Framework` 2026.1.31 restores `Metalama.Compiler` 2026.1.21. + +### The rows, console project, RC3 the only SDK + +| Row | Build | Diagnostics | Run | RC1 (decision 120) | +|---|---|---|---|---| +| control: no TypeKit, no Metalama, a declared union and a library union matched | passes | none | `declared union: circle 1,5`, `matched union: cat Tom` | same | +| 1. value object, no union | passes | none | `From` prints the `Guid`, `Parse` equal, JSON `"0b9f7d3a-..."` read back equal | builds and weaves | +| 2. `#error version` | fails, as meant | `CS8304`: Compiler version '2026.1.20 (4a1b04c1...)'. Language version: 14.0 | | 2026.1.20 at 14.0 | +| 2b. `#error version`, `Metalama.Framework` 2026.1.31 | fails, as meant | `CS8304`: '2026.1.21 (c1b05ff9...)'. Language version: 14.0 | | not measured | +| 3. `public union Shape(Circle, Square)` in the project | fails | `CS0106` the modifier 'public' is not valid; `CS1001` identifier expected; `CS8803` top-level statements must precede namespace and type declarations | | `CS0106`, `CS8803`, `CS1001` | +| 4a. library union held in a `var` and printed | passes | none | `held: UnionLib.Pet` | builds | +| 4b. `switch` over the library union | fails | `CS8652`: The feature 'unions' is currently in Preview and *unsupported* | | `CS8652` | +| 5. warnings in rows 1 to 4 | | none: no `LAMA0617`, no other warning | | `LAMA0617` reported on a web project | +| 6. row 1, `Metalama.Framework` 2027.0.5-preview | passes | none | as row 1 | builds and weaves (2027.0.4-preview) | +| 6. row 2, 2027.0.5-preview | fails, as meant | `CS8304`: '2027.0.5-preview (dc7c9a1c...)'. Language version: 15.0 | | 2027.0.4-preview at 15.0 | +| 6. row 3, 2027.0.5-preview | passes | none | as row 1, then `declared union: circle 1,5` | builds and runs | +| 6. row 4b, 2027.0.5-preview | passes | none | as row 1, then `matched union: cat Tom` | not measured | + +### Row 5 again: the web project, by installation + +| Installation | Metalama 2026.1 (2.3.1's) | `Metalama.Framework` 2026.1.31 | `Metalama.Framework` 2027.0.5-preview | +|---|---|---|---| +| RC3 alone | **fails**: `LAMA0625`, plus `LAMA0617` and `CS8034` | **fails**: the same | passes, no warning | +| RC3 with SDK 10.0.403 beside | passes: `LAMA0617` twice | not run | passes, no warning | +| RC3 alone, `net10.0` | **fails**: `LAMA0625` | not run | passes, no warning | +| RC1 alone | **fails**: `LAMA0625` | not run | passes, no warning | +| SDK 10.0.403, `net10.0` (today) | passes, no warning | not run | passes, no warning | + +The diagnostics, as reported on RC3 alone: + +- `error LAMA0625`: Analyzer 'Microsoft.CodeAnalysis.Razor.Compiler.dll' from .NET SDK 11.0.100-rc.3.26522.148 + requires Roslyn 5.12.0.0, newer than the Roslyn 5.9.0.0 bundled with Metalama Compiler. Metalama searched all .NET + SDKs installed on this machine for a compatible older version of 'Microsoft.CodeAnalysis.Razor.Compiler.dll', but + none qualifies. To fix, install a .NET SDK whose 'Microsoft.CodeAnalysis.Razor.Compiler.dll' references Roslyn + 5.9.0.0 or earlier and pin it in global.json, or upgrade Metalama Compiler to a version supporting Roslyn 5.12.0.0. + Installed SDKs found: 11.0.100. +- `warning LAMA0617`: Analyzer 'Microsoft.AspNetCore.App.Analyzers.dll' from .NET SDK (unknown) requires Roslyn + 5.11.0.0, newer than the Roslyn 5.9.0.0 bundled with Metalama Compiler. Metalama supplied a compatible version of + 'Microsoft.AspNetCore.App.Analyzers.dll' from .NET SDK 10.0.401 instead. +- `warning CS8034`: Unable to load Analyzer assembly + `metalama.compiler/2026.1.20/sdkAnalyzers/Microsoft.AspNetCore.Razor.Utilities.Shared.dll`: Could not load file or + assembly 'Microsoft.AspNetCore.Razor.Utilities.Shared, Version=10.4.0.0'. + +On RC1 alone the Razor compiler needs Roslyn 5.11.0.0 and `LAMA0625` is the only diagnostic. With SDK 10.0.403 +beside RC3, the two `LAMA0617` warnings name the ASP.NET analyzers taken from SDK 10.0.401 and the Razor compiler +taken from the SDK 10.0.403 beside it. + +`Metalama.Compiler` 2026.1.20 carries copies of the SDK's analyzers and generators in its `sdkAnalyzers` folder, +`Microsoft.CodeAnalysis.Razor.Compiler.dll` among them; the `CS8034` above is the load of one of them failing. Whether +that failure is what leaves `LAMA0625` without a fallback is not established here. + +"Installed SDKs found: 11.0.100" while this machine has SDK 10.0.403 in `/usr/local/share/dotnet` and 10.0.303 in +`~/.dotnet`: the search covers the .NET installation the build runs from, not the machine. + +## Limits + +- This machine has a Metalama licensing file with an entry for 2027.0, so the 2027 cells say nothing about a build + without a license. The file was not changed by these runs. #170 keeps the license-free CI job. +- macOS arm64 only, the command line only (no IDE), and console and minimal-API projects only. The general + availability SDK, Linux, Windows and a Razor Pages or Blazor project are not measured. +- An SDK 11 container image (`mcr.microsoft.com/dotnet/sdk:11.0`) holds one SDK, so it is the "RC3 alone" cell by + construction; it was not run. +- Not repeated here: the repository's own build on SDK 11, the woven `net10.0` assets compared, the analyzers on the + SDK 11 compiler, and the NU1605 measurement. They remain #170's. diff --git a/spikes/Sdk11Rc3Metalama/UnionLib/Pets.cs b/spikes/Sdk11Rc3Metalama/UnionLib/Pets.cs new file mode 100644 index 00000000..e338c6a0 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/UnionLib/Pets.cs @@ -0,0 +1,12 @@ +namespace UnionLib; + +public sealed record Cat(string Name); + +public sealed record Dog(string Name); + +public union Pet(Cat, Dog); + +public static class Pets +{ + public static Pet Adopt() => new Cat("Tom"); +} diff --git a/spikes/Sdk11Rc3Metalama/UnionLib/UnionLib.csproj b/spikes/Sdk11Rc3Metalama/UnionLib/UnionLib.csproj new file mode 100644 index 00000000..907d78c5 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/UnionLib/UnionLib.csproj @@ -0,0 +1,7 @@ + + + + net11.0 + enable + + diff --git a/spikes/Sdk11Rc3Metalama/WebApp/Program.cs b/spikes/Sdk11Rc3Metalama/WebApp/Program.cs new file mode 100644 index 00000000..da23d072 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/WebApp/Program.cs @@ -0,0 +1,7 @@ +using CodoMetis.TypeKit.ValueObjects; + +var app = WebApplication.CreateBuilder(args).Build(); +app.MapGet("/orders/{id}", (OrderId id) => id); +app.Run(); + +public readonly partial record struct OrderId : IValue; diff --git a/spikes/Sdk11Rc3Metalama/WebApp/WebApp.csproj b/spikes/Sdk11Rc3Metalama/WebApp/WebApp.csproj new file mode 100644 index 00000000..6a4c097d --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/WebApp/WebApp.csproj @@ -0,0 +1,21 @@ + + + + net11.0 + enable + enable + false + + $(NoWarn);ASP0020 + + + + + + + + + diff --git a/spikes/Sdk11Rc3Metalama/global.json b/spikes/Sdk11Rc3Metalama/global.json new file mode 100644 index 00000000..9146b739 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/global.json @@ -0,0 +1,11 @@ +{ + "//": [ + "The .NET 11 release candidate 3 SDK, exactly: the measurement is of this build and no other.", + "It is installed side by side (README, \"Running it\"), so the machine's own SDKs stay as they are." + ], + "sdk": { + "version": "11.0.100-rc.3.26522.148", + "rollForward": "disable", + "allowPrerelease": true + } +} diff --git a/spikes/Sdk11Rc3Metalama/nuget.config b/spikes/Sdk11Rc3Metalama/nuget.config new file mode 100644 index 00000000..613f1b99 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/nuget.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/spikes/Sdk11Rc3Metalama/run.sh b/spikes/Sdk11Rc3Metalama/run.sh new file mode 100755 index 00000000..20b5fa05 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/run.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Runs every row of the README's table on the RC3 SDK and writes one log per row to $1. +# Usage: DOTNET=~/.dotnet-sdk11-rc3/dotnet NUGET_PACKAGES= ./run.sh +set -u +here="$(cd "$(dirname "$0")" && pwd)" +out="${1:?out-dir}" +dotnet="${DOTNET:?the RC3 dotnet}" +export DOTNET_ROOT="$(dirname "$dotnet")" +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_CLI_UI_LANGUAGE=en +mkdir -p "$out" +cd "$here" + +row() { + local name="$1" project="$2"; shift 2 + rm -rf App/bin App/obj UnionLib/bin UnionLib/obj WebApp/bin WebApp/obj + "$dotnet" build-server shutdown > /dev/null 2>&1 + pkill -f VBCSCompiler 2> /dev/null; pkill -f -i 'metalama' 2> /dev/null + "$dotnet" build "$project/$project.csproj" -p:UseSharedCompilation=false -nodeReuse:false "$@" > "$out/$name.log" 2>&1 + local code=$? + { + echo "== $name: $project $* -> build exit $code" + grep -oE '(error|warning) [A-Z]+[0-9]+: [^[]*' "$out/$name.log" | sort -u + if [ $code -eq 0 ] && [ "$project" = App ]; then + echo "-- run" + "$dotnet" App/bin/Debug/net11.0/App.dll 2>&1 + fi + } | tee -a "$out/summary.txt" +} + +"$dotnet" --version | tee "$out/summary.txt" +row control-no-metalama App -p:NoGenerators=true -p:DeclareUnion=true -p:MatchUnion=true +row 1-plain App +row 2-version App -p:ProbeVersion=true +row 2b-version-2026.1.31 App -p:MetalamaFramework=2026.1.31 -p:ProbeVersion=true +row 3-declare-union App -p:DeclareUnion=true +row 4a-hold-library-union App -p:HoldUnion=true +row 4b-match-library-union App -p:MatchUnion=true +row 6-2027-plain App -p:MetalamaFramework=2027.0.5-preview +row 6-2027-version App -p:MetalamaFramework=2027.0.5-preview -p:ProbeVersion=true +row 6-2027-declare-union App -p:MetalamaFramework=2027.0.5-preview -p:DeclareUnion=true +row 6-2027-match-union App -p:MetalamaFramework=2027.0.5-preview -p:MatchUnion=true diff --git a/spikes/Sdk11Rc3Metalama/web-matrix.sh b/spikes/Sdk11Rc3Metalama/web-matrix.sh new file mode 100755 index 00000000..9db464ab --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/web-matrix.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# Builds WebApp, with Metalama 2026.1 and with 2027, under three .NET installations, one log per cell in $1: +# rc3-alone RC3 the only SDK in its dotnet root ($RC3_ALONE) +# rc3-beside RC3 with SDK 10.0.403 installed in the same dotnet root ($RC3_BESIDE) +# sdk10 SDK 10.0.403 from that root, net10.0 (today's consumer), in a copy pinned to it +# rc1-alone RC1 the only SDK in its dotnet root ($RC1_ALONE), in a copy pinned to it +# rc3-net10 RC3 alone again, building net10.0: today's 2.x consumer once it moves to SDK 11 +# Usage: RC3_ALONE=~/.dotnet-sdk11-rc3/dotnet RC3_BESIDE=~/.dotnet-sdk11-rc3-sdk10/dotnet \ +# RC1_ALONE=~/.dotnet-sdk11-rc1/dotnet NUGET_PACKAGES= ./web-matrix.sh +set -u +here="$(cd "$(dirname "$0")" && pwd)" +out="${1:?out-dir}" +mkdir -p "$out" +export DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_CLI_UI_LANGUAGE=en + +# The SDK 10 and RC1 cells: the same project, in a copy whose global.json pins that SDK. +pinned() { + local dir="$out/$1-copy" + rm -rf "$dir"; mkdir -p "$dir" + cp -R "$here/WebApp" "$here/Directory.Build.props" "$here/nuget.config" "$dir/" + printf '{ "sdk": { "version": "%s", "rollForward": "disable", "allowPrerelease": true } }\n' "$2" > "$dir/global.json" + echo "$dir" +} +copy="$(pinned sdk10 10.0.403)" +sed -i.bak 's|net11.0|net10.0|' "$copy/WebApp/WebApp.csproj" +rc1="$(pinned rc1 11.0.100-rc.1.26425.128)" +net10="$(pinned rc3-net10 11.0.100-rc.3.26522.148)" +sed -i.bak 's|net11.0|net10.0|' "$net10/WebApp/WebApp.csproj" + +cell() { + local name="$1" dotnet="$2" dir="$3"; shift 3 + export DOTNET_ROOT="$(dirname "$dotnet")" + rm -rf "$dir/WebApp/bin" "$dir/WebApp/obj" + "$dotnet" build-server shutdown > /dev/null 2>&1 + pkill -f VBCSCompiler 2> /dev/null; pkill -f -i 'metalama' 2> /dev/null + (cd "$dir" && "$dotnet" build WebApp/WebApp.csproj -p:UseSharedCompilation=false -nodeReuse:false "$@") > "$out/$name.log" 2>&1 + local code=$? + { + echo "== $name: $(cd "$dir" && "$dotnet" --version) in $DOTNET_ROOT $* -> build exit $code" + grep -oE '(error|warning) [A-Z]+[0-9]+: [^[]*' "$out/$name.log" | sort -u + } | tee -a "$out/summary.txt" +} + +: > "$out/summary.txt" +cell rc3-alone-2026 "${RC3_ALONE:?}" "$here" +cell rc3-alone-2027 "$RC3_ALONE" "$here" -p:MetalamaFramework=2027.0.5-preview +cell rc3-alone-2026.1.31 "$RC3_ALONE" "$here" -p:MetalamaFramework=2026.1.31 +cell rc3-beside-2026 "${RC3_BESIDE:?}" "$here" +cell rc3-beside-2027 "$RC3_BESIDE" "$here" -p:MetalamaFramework=2027.0.5-preview +cell sdk10-2026 "$RC3_BESIDE" "$copy" +cell sdk10-2027 "$RC3_BESIDE" "$copy" -p:MetalamaFramework=2027.0.5-preview +cell rc1-alone-2026 "${RC1_ALONE:?}" "$rc1" +cell rc1-alone-2027 "$RC1_ALONE" "$rc1" -p:MetalamaFramework=2027.0.5-preview +cell rc3-net10-2026 "$RC3_ALONE" "$net10" +cell rc3-net10-2027 "$RC3_ALONE" "$net10" -p:MetalamaFramework=2027.0.5-preview From babc5657837a793a6629118086a8bbd876b06ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Gro=C3=9F?= Date: Sat, 31 Oct 2026 21:02:53 +0100 Subject: [PATCH 2/2] spike: one project on net10.0 and net11.0 with Metalama 2027 Adds a multi-targeted consumer to the RC3 spike: with a direct Metalama.Framework 2027.0.5-preview reference and no LangVersion, net10.0 compiles at C# 14.0 and net11.0 at 15.0. A union under #if NET11_0_OR_GREATER, or in a file removed from net10.0, builds on both targets, and the value object is generated and runs on both. On 2026.1 both targets are at 14.0 and either union fails on net11.0. Refs #170 Co-Authored-By: Claude Opus 5.5 --- ...B-060-metalama-2026-1-on-the-net-11-sdk.md | 6 +++- spikes/Sdk11Rc3Metalama/Multi/Multi.csproj | 32 +++++++++++++++++ .../Multi/Net11Only/Shapes.cs | 14 ++++++++ spikes/Sdk11Rc3Metalama/Multi/Program.cs | 34 +++++++++++++++++++ spikes/Sdk11Rc3Metalama/README.md | 30 ++++++++++++++-- spikes/Sdk11Rc3Metalama/run.sh | 23 +++++++++++-- 6 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 spikes/Sdk11Rc3Metalama/Multi/Multi.csproj create mode 100644 spikes/Sdk11Rc3Metalama/Multi/Net11Only/Shapes.cs create mode 100644 spikes/Sdk11Rc3Metalama/Multi/Program.cs diff --git a/design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md b/design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md index 2b4a3f9a..95b8a779 100644 --- a/design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md +++ b/design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md @@ -22,8 +22,12 @@ On SDK 11.0.100-rc.3, with the 2.3.1 packages (`Metalama.Framework` 2026.1.30, ` both compile. - A direct `Metalama.Framework` 2027.0.5-preview reference beside the 2.3.1 packages: compiler 2027.0.5-preview at 15.0, the value object woven, and both union cases build and run. +- One project on `net10.0;net11.0` with that 2027 reference and no `LangVersion`: `net10.0` compiles at 14.0 and + `net11.0` at 15.0. A union under `#if NET11_0_OR_GREATER`, or in a file removed from `net10.0` by a conditional + `Compile Remove`, builds on both targets, and the value object is generated and runs on both. With 2026.1 both + targets compile at 14.0 and either union fails on `net11.0`. -These are the RC1 results of decision 120, unchanged. +The first four are the RC1 results of decision 120, unchanged; decision 120 did not measure the multi-targeted project. **A web project (`Microsoft.NET.Sdk.Web`) on Metalama 2026.1 fails with `LAMA0625` when SDK 11 is the only SDK in its .NET installation**, on RC1 and on RC3, with 2026.1.31, and for a `net10.0` project as for a `net11.0` one. The SDK 11 Razor compiler needs Roslyn 5.11 (RC1) or diff --git a/spikes/Sdk11Rc3Metalama/Multi/Multi.csproj b/spikes/Sdk11Rc3Metalama/Multi/Multi.csproj new file mode 100644 index 00000000..3b6501a5 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/Multi/Multi.csproj @@ -0,0 +1,32 @@ + + + + Exe + net10.0;net11.0 + enable + enable + false + + + $(DefineConstants);PROBE_VERSION + $(DefineConstants);IF_UNION + $(DefineConstants);EXCLUDED_FILE + + + + + + + + + + + + + diff --git a/spikes/Sdk11Rc3Metalama/Multi/Net11Only/Shapes.cs b/spikes/Sdk11Rc3Metalama/Multi/Net11Only/Shapes.cs new file mode 100644 index 00000000..70607493 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/Multi/Net11Only/Shapes.cs @@ -0,0 +1,14 @@ +public sealed record Circle(double Radius); + +public sealed record Square(double Side); + +public union Shape(Circle, Square); + +public static class Shapes +{ + public static string Describe(Shape shape) => shape switch + { + Circle c => $"circle {c.Radius}", + Square s => $"square {s.Side}", + }; +} diff --git a/spikes/Sdk11Rc3Metalama/Multi/Program.cs b/spikes/Sdk11Rc3Metalama/Multi/Program.cs new file mode 100644 index 00000000..2e2e7ca7 --- /dev/null +++ b/spikes/Sdk11Rc3Metalama/Multi/Program.cs @@ -0,0 +1,34 @@ +#if PROBE_VERSION +#error version +#endif + +using System.Text.Json; +using CodoMetis.TypeKit.ValueObjects; + +var target = AppContext.TargetFrameworkName; +var id = OrderId.From(Guid.Parse("0b9f7d3a-5c1e-4f5a-9d8e-2a7c6b4e1f03")); +var json = JsonSerializer.Serialize(id); +Console.WriteLine($"{target}: From {id}, Parse equal {OrderId.Parse(id.ToString(), null) == id}, JSON {json} read back equal {JsonSerializer.Deserialize(json) == id}"); + +#if IF_UNION && NET11_0_OR_GREATER +Pet pet = new Cat("Tom"); +Console.WriteLine(pet switch +{ + Cat c => $"{target}: union under #if: cat {c.Name}", + Dog d => $"{target}: union under #if: dog {d.Name}", +}); +#endif + +#if EXCLUDED_FILE && NET11_0_OR_GREATER +Console.WriteLine($"{target}: union in a net11.0-only file: {Shapes.Describe(new Circle(1.5))}"); +#endif + +public readonly partial record struct OrderId : IValue; + +#if IF_UNION && NET11_0_OR_GREATER +public sealed record Cat(string Name); + +public sealed record Dog(string Name); + +public union Pet(Cat, Dog); +#endif diff --git a/spikes/Sdk11Rc3Metalama/README.md b/spikes/Sdk11Rc3Metalama/README.md index 9306e273..6dba6c5b 100644 --- a/spikes/Sdk11Rc3Metalama/README.md +++ b/spikes/Sdk11Rc3Metalama/README.md @@ -13,11 +13,18 @@ measurement, made on SDK 11.0.100-rc.1 on 2026-10-29, repeated on release candid 4. what does a `switch` over a union returned by a `net11.0`-only library give? 5. is LAMA0617 still reported, and is anything new? 6. what changes with a direct `Metalama.Framework` 2027 reference beside the 2.3.1 packages? +7. in one project targeting `net10.0;net11.0` with that 2027 reference and no `LangVersion`: which language version + does each target compile at, does C# 15 under `#if NET11_0_OR_GREATER` or in a file removed from `net10.0` build on + both targets, and is the value object generated on both? **Answer: every RC1 result holds on RC3.** Metalama 2026.1 builds, weaves and runs a `net11.0` value object, its compiler (2026.1.20) runs the project at C# 14, a declared union fails with `CS0106`, `CS8803` and `CS1001`, and a `switch` over a library's union with `CS8652`. With `Metalama.Framework` 2027.0.5-preview the compiler is 2027 at C# 15 and both union rows build and run. The latest 2026.1 patch (2026.1.31, compiler 2026.1.21) is also at C# 14. +In one project on `net10.0;net11.0` with 2027.0.5-preview and no `LangVersion`, each target takes its SDK default: +`net10.0` compiles at 14.0 and `net11.0` at 15.0, with the same compiler. A union under `#if NET11_0_OR_GREATER`, or in +a file the project removes from `net10.0`, builds on both targets, and the value object is generated and runs on both. +On 2026.1 the same project compiles both targets at 14.0, and either union fails on `net11.0`. **One finding that the RC1 runs could not show, and that is not an RC3 change.** A web project (`Microsoft.NET.Sdk.Web`, which loads the Razor source generator) on Metalama 2026.1 **fails** with `LAMA0625` @@ -35,6 +42,9 @@ Filed as [#275](https://github.com/CaffeinatedCoder/CodoMetis.TypeKit/issues/275 - `App`: a `net11.0` console project on `CodoMetis.TypeKit` and `CodoMetis.TypeKit.Generators` 2.3.1, with `OrderId : IValue`. Properties switch the rows (`App.csproj` lists them): `ProbeVersion`, `DeclareUnion`, `HoldUnion`, `MatchUnion`, `MetalamaFramework=` (a direct reference) and `NoGenerators` (the control). +- `Multi`: one project on `net10.0;net11.0`, no `LangVersion`, with the value object. `ProbeVersion`, + `MetalamaFramework=`, `IfUnion` (a union and a `switch` inside `#if NET11_0_OR_GREATER`) and `ExcludedFile` + (`Net11Only/Shapes.cs`, a union, under ``). - `WebApp`: the same value object as a minimal-API route parameter, in `Microsoft.NET.Sdk.Web`. It suppresses ASP0020, the documented false alarm on a route parameter ([decision 12](../../design/adr/0012-asp0020-documented-suppressed.md)), which fires on SDK 10 as on SDK 11. @@ -55,12 +65,14 @@ The SDKs are installed side by side with Microsoft's script, leaving `/usr/local ``` ```bash -DOTNET=~/.dotnet-sdk11-rc3/dotnet NUGET_PACKAGES= spikes/Sdk11Rc3Metalama/run.sh +DOTNET=~/.dotnet-sdk11-rc3/dotnet DOTNET10=~/.dotnet-sdk11-rc3-sdk10/dotnet NUGET_PACKAGES= \ + spikes/Sdk11Rc3Metalama/run.sh RC3_ALONE=~/.dotnet-sdk11-rc3/dotnet RC3_BESIDE=~/.dotnet-sdk11-rc3-sdk10/dotnet RC1_ALONE=~/.dotnet-sdk11-rc1/dotnet \ NUGET_PACKAGES= spikes/Sdk11Rc3Metalama/web-matrix.sh ``` -`run.sh` builds `App` once per row and runs what builds; `web-matrix.sh` builds `WebApp` under each installation, +`run.sh` builds `App` or `Multi` once per row on RC3 alone and runs what builds (`Multi`'s `net10.0` output on the +`DOTNET10` host, since RC3 alone has no .NET 10 runtime); `web-matrix.sh` builds `WebApp` under each installation, the SDK 10, RC1 and `net10.0` cells from a copy whose `global.json` pins that SDK. Each writes a log per row and `summary.txt`. ## Evidence @@ -86,6 +98,20 @@ SDK 11.0.100-rc.3.26522.148 (runtime 11.0.0-rc.3.26522.148), macOS 27.0.1 on arm | 6. row 3, 2027.0.5-preview | passes | none | as row 1, then `declared union: circle 1,5` | builds and runs | | 6. row 4b, 2027.0.5-preview | passes | none | as row 1, then `matched union: cat Tom` | not measured | +### Row 7: one project on `net10.0;net11.0`, RC3 the only SDK + +Diagnostics carry their target in braces. The 2026.1 rows are the contrast, with no direct Metalama reference. + +| Row | Build | Diagnostics | Run | +|---|---|---|---| +| `#error version`, 2027.0.5-preview | fails, as meant | `CS8304`: '2027.0.5-preview (dc7c9a1c...)'. Language version: 14.0 {net10.0}; the same compiler at 15.0 {net11.0} | | +| value object, 2027.0.5-preview | passes | none | both targets: `From`, `Parse` equal, JSON read back equal | +| union and `switch` under `#if NET11_0_OR_GREATER`, 2027.0.5-preview | passes | none | both targets as above, then `union under #if: cat Tom` {net11.0} | +| union in `Net11Only/Shapes.cs`, removed on `net10.0`, 2027.0.5-preview | passes | none | both targets as above, then `union in a net11.0-only file: circle 1,5` {net11.0} | +| `#error version`, 2026.1 | fails, as meant | `CS8304`: '2026.1.20 (4a1b04c1...)'. Language version: 14.0, on both targets | | +| union under `#if`, 2026.1 | fails | `CS0106`, `CS1001`, `CS8803` {net11.0} | | +| union in the `net11.0`-only file, 2026.1 | fails | `CS0106`, `CS1001`, `CS8803` {net11.0} | | + ### Row 5 again: the web project, by installation | Installation | Metalama 2026.1 (2.3.1's) | `Metalama.Framework` 2026.1.31 | `Metalama.Framework` 2027.0.5-preview | diff --git a/spikes/Sdk11Rc3Metalama/run.sh b/spikes/Sdk11Rc3Metalama/run.sh index 20b5fa05..109e876e 100755 --- a/spikes/Sdk11Rc3Metalama/run.sh +++ b/spikes/Sdk11Rc3Metalama/run.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash # Runs every row of the README's table on the RC3 SDK and writes one log per row to $1. -# Usage: DOTNET=~/.dotnet-sdk11-rc3/dotnet NUGET_PACKAGES= ./run.sh +# Usage: DOTNET=~/.dotnet-sdk11-rc3/dotnet DOTNET10=~/.dotnet-sdk11-rc3-sdk10/dotnet \ +# NUGET_PACKAGES= ./run.sh +# DOTNET10 is a host with the .NET 10 runtime, to run Multi's net10.0 output; RC3 alone has none. set -u here="$(cd "$(dirname "$0")" && pwd)" out="${1:?out-dir}" @@ -13,18 +15,26 @@ cd "$here" row() { local name="$1" project="$2"; shift 2 - rm -rf App/bin App/obj UnionLib/bin UnionLib/obj WebApp/bin WebApp/obj + rm -rf App/bin App/obj UnionLib/bin UnionLib/obj Multi/bin Multi/obj WebApp/bin WebApp/obj "$dotnet" build-server shutdown > /dev/null 2>&1 pkill -f VBCSCompiler 2> /dev/null; pkill -f -i 'metalama' 2> /dev/null "$dotnet" build "$project/$project.csproj" -p:UseSharedCompilation=false -nodeReuse:false "$@" > "$out/$name.log" 2>&1 local code=$? { echo "== $name: $project $* -> build exit $code" - grep -oE '(error|warning) [A-Z]+[0-9]+: [^[]*' "$out/$name.log" | sort -u + # A multi-targeted project's diagnostic keeps its target as {net10.0}; the project path is dropped. + grep -E '(error|warning) [A-Z]+[0-9]+: ' "$out/$name.log" \ + | sed -E 's/^.*((error|warning) [A-Z]+[0-9]+: )/\1/; s/ \[[^]]*::TargetFramework=([^]]+)\]$/ {\1}/; s/ \[[^]]*\]$//' \ + | sort -u if [ $code -eq 0 ] && [ "$project" = App ]; then echo "-- run" "$dotnet" App/bin/Debug/net11.0/App.dll 2>&1 fi + if [ $code -eq 0 ] && [ "$project" = Multi ]; then + echo "-- run" + DOTNET_ROOT="$(dirname "${DOTNET10:?a host with the .NET 10 runtime}")" "$DOTNET10" Multi/bin/Debug/net10.0/Multi.dll 2>&1 + "$dotnet" Multi/bin/Debug/net11.0/Multi.dll 2>&1 + fi } | tee -a "$out/summary.txt" } @@ -40,3 +50,10 @@ row 6-2027-plain App -p:MetalamaFramework=2027.0.5-preview row 6-2027-version App -p:MetalamaFramework=2027.0.5-preview -p:ProbeVersion=true row 6-2027-declare-union App -p:MetalamaFramework=2027.0.5-preview -p:DeclareUnion=true row 6-2027-match-union App -p:MetalamaFramework=2027.0.5-preview -p:MatchUnion=true +row 7-multi-2027-version Multi -p:MetalamaFramework=2027.0.5-preview -p:ProbeVersion=true +row 7-multi-2027-plain Multi -p:MetalamaFramework=2027.0.5-preview +row 7-multi-2027-if-union Multi -p:MetalamaFramework=2027.0.5-preview -p:IfUnion=true +row 7-multi-2027-excluded-file Multi -p:MetalamaFramework=2027.0.5-preview -p:ExcludedFile=true +row 7-multi-2026-version Multi -p:ProbeVersion=true +row 7-multi-2026-if-union Multi -p:IfUnion=true +row 7-multi-2026-excluded-file Multi -p:ExcludedFile=true