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
41 changes: 41 additions & 0 deletions design/knowledge/KB-060-metalama-2026-1-on-the-net-11-sdk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
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.
- 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`.

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
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).
1 change: 1 addition & 0 deletions design/knowledge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` 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 |
35 changes: 35 additions & 0 deletions spikes/Sdk11Rc3Metalama/App/App.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<!--
One consumer, switched per row by properties (README, "The rows"):
-p:ProbeVersion=true #error version, which names the compiler and its language version
-p:DeclareUnion=true a union declared here, and a switch over it
-p:HoldUnion=true UnionLib's union held in a var and printed
-p:MatchUnion=true a switch over UnionLib's union
-p:MetalamaFramework=V a direct Metalama.Framework V reference beside the 2.3.1 packages
-p:NoGenerators=true the control: no TypeKit, no Metalama, no value object
-->
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseSharedCompilation>false</UseSharedCompilation>
</PropertyGroup>
<PropertyGroup>
<DefineConstants Condition="'$(ProbeVersion)' == 'true'">$(DefineConstants);PROBE_VERSION</DefineConstants>
<DefineConstants Condition="'$(DeclareUnion)' == 'true'">$(DefineConstants);DECLARE_UNION</DefineConstants>
<DefineConstants Condition="'$(HoldUnion)' == 'true'">$(DefineConstants);HOLD_UNION</DefineConstants>
<DefineConstants Condition="'$(MatchUnion)' == 'true'">$(DefineConstants);MATCH_UNION</DefineConstants>
<DefineConstants Condition="'$(NoGenerators)' == 'true'">$(DefineConstants);NO_GENERATORS</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(NoGenerators)' != 'true'">
<PackageReference Include="CodoMetis.TypeKit" Version="2.3.1" />
<PackageReference Include="CodoMetis.TypeKit.Generators" Version="2.3.1" />
</ItemGroup>
<ItemGroup Condition="'$(MetalamaFramework)' != ''">
<PackageReference Include="Metalama.Framework" Version="$(MetalamaFramework)" />
</ItemGroup>
<ItemGroup Condition="'$(HoldUnion)' == 'true' Or '$(MatchUnion)' == 'true'">
<ProjectReference Include="../UnionLib/UnionLib.csproj" />
</ItemGroup>
</Project>
54 changes: 54 additions & 0 deletions spikes/Sdk11Rc3Metalama/App/Program.cs
Original file line number Diff line number Diff line change
@@ -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<OrderId>(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<Guid>;
#endif

#if DECLARE_UNION
public sealed record Circle(double Radius);

public sealed record Square(double Side);

public union Shape(Circle, Square);
#endif
3 changes: 3 additions & 0 deletions spikes/Sdk11Rc3Metalama/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Project>
<!-- Self-contained spike: nothing above this folder is inherited. -->
</Project>
32 changes: 32 additions & 0 deletions spikes/Sdk11Rc3Metalama/Multi/Multi.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<!--
One project on both targets, with no LangVersion: each target takes its SDK default. Rows:
-p:MetalamaFramework=V a direct Metalama.Framework V reference beside the 2.3.1 packages
-p:ProbeVersion=true #error version, once per target
-p:IfUnion=true a union and a switch over it inside #if NET11_0_OR_GREATER
-p:ExcludedFile=true Net11Only/Shapes.cs, a union, compiled on net11.0 only (Compile Remove on net10.0)
-->
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0;net11.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseSharedCompilation>false</UseSharedCompilation>
</PropertyGroup>
<PropertyGroup>
<DefineConstants Condition="'$(ProbeVersion)' == 'true'">$(DefineConstants);PROBE_VERSION</DefineConstants>
<DefineConstants Condition="'$(IfUnion)' == 'true'">$(DefineConstants);IF_UNION</DefineConstants>
<DefineConstants Condition="'$(ExcludedFile)' == 'true'">$(DefineConstants);EXCLUDED_FILE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CodoMetis.TypeKit" Version="2.3.1" />
<PackageReference Include="CodoMetis.TypeKit.Generators" Version="2.3.1" />
</ItemGroup>
<ItemGroup Condition="'$(MetalamaFramework)' != ''">
<PackageReference Include="Metalama.Framework" Version="$(MetalamaFramework)" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Net11Only/**" Condition="'$(TargetFramework)' == 'net10.0'" />
<Compile Remove="Net11Only/**" Condition="'$(ExcludedFile)' != 'true'" />
</ItemGroup>
</Project>
14 changes: 14 additions & 0 deletions spikes/Sdk11Rc3Metalama/Multi/Net11Only/Shapes.cs
Original file line number Diff line number Diff line change
@@ -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}",
};
}
34 changes: 34 additions & 0 deletions spikes/Sdk11Rc3Metalama/Multi/Program.cs
Original file line number Diff line number Diff line change
@@ -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<OrderId>(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<Guid>;

#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
Loading
Loading