diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d88912..af34433 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -117,12 +117,14 @@ dotnet run -c Release --project benchmarks/DotMaybe.Benchmarks -- --filter '*Cha - **CodeQL** (workflow *CodeQL*): static analysis on pull requests, on `main` and weekly; findings are listed under *Security > Code scanning*. -## Updating the .NET SDK (RC1 → RC2 → GA) +## Updating the .NET SDK 1. Bump `sdk.version` in `global.json` to the exact SDK version. 2. Read the C# release notes for changes to unions. 3. Run the compiler contract tests first. They are the ones that notice changes in union behavior. -4. Update test dependencies if needed. +4. Refresh the lock files (`dotnet restore dotMaybe.slnx --force-evaluate`): the SDK's implicit packages, such as + `Microsoft.NET.ILLink.Tasks`, follow its version. +5. Update test dependencies if needed. ## One-time repository setup diff --git a/README.md b/README.md index ef403ca..24459b8 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ var number = answer.OrDefault(0); var lazy = answer.OrDefault(() => ExpensiveFallback()); ``` -In generic code use `Match`: C# does not allow `maybe is T value` when `T` is a type parameter. +Pattern matching works in generic code too: `maybe is T value` reads the value when `T` is a type parameter. ### Transforming and chaining diff --git a/global.json b/global.json index 7903a71..269797b 100644 --- a/global.json +++ b/global.json @@ -1,8 +1,8 @@ { "sdk": { - "version": "11.0.100-rc.3.26522.148", + "version": "11.0.100", "rollForward": "latestFeature", - "allowPrerelease": true + "allowPrerelease": false }, "test": { "runner": "Microsoft.Testing.Platform" diff --git a/src/DotMaybe/DotMaybe.csproj b/src/DotMaybe/DotMaybe.csproj index c860897..ec5318c 100644 --- a/src/DotMaybe/DotMaybe.csproj +++ b/src/DotMaybe/DotMaybe.csproj @@ -1,7 +1,6 @@ - preview DotMaybe DotMaybe true diff --git a/src/DotMaybe/Maybe.cs b/src/DotMaybe/Maybe.cs index 5b82c10..069daa1 100644 --- a/src/DotMaybe/Maybe.cs +++ b/src/DotMaybe/Maybe.cs @@ -107,7 +107,7 @@ bool IUnionMembers.TryGetValue(out None value) /// runs when there is none. Exactly one of them runs, exactly once. /// /// - /// This is the way to read a value in generic code, where maybe is T value does not compile. + /// The expression form of maybe switch { T value => ..., None => ... }, convenient in lambdas and chains. /// /// The type of the result. /// Produces the result from the value. diff --git a/src/DotMaybe/packages.lock.json b/src/DotMaybe/packages.lock.json index 43d8683..8c91a64 100644 --- a/src/DotMaybe/packages.lock.json +++ b/src/DotMaybe/packages.lock.json @@ -10,9 +10,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[11.0.0-rc.3.26522.148, )", - "resolved": "11.0.0-rc.3.26522.148", - "contentHash": "SWnTMx96mhBr9LCOySJg1ETlEFFHVsPGdVT02JnYhtqk1+Vke1J7RU3VkKwEiErBr4FJ42Uu4k7GdOtkRuvs/Q==" + "requested": "[11.0.0, )", + "resolved": "11.0.0", + "contentHash": "gK2aq4XvChj5kvozpPHrkfKkr7zc5Z4xoTrJb4GPZzDEPMlCk0TX632bwY3LsWcX2UVcVRQBBGzsBGC2lq6YhA==" }, "MinVer": { "type": "Direct", diff --git a/tests/DotMaybe.CompilerContractTests/DotMaybe.CompilerContractTests.csproj b/tests/DotMaybe.CompilerContractTests/DotMaybe.CompilerContractTests.csproj index 3bd5a03..bc8a934 100644 --- a/tests/DotMaybe.CompilerContractTests/DotMaybe.CompilerContractTests.csproj +++ b/tests/DotMaybe.CompilerContractTests/DotMaybe.CompilerContractTests.csproj @@ -6,7 +6,7 @@ diff --git a/tests/DotMaybe.CompilerContractTests/PatternMatchingContracts.cs b/tests/DotMaybe.CompilerContractTests/PatternMatchingContracts.cs index 45c6608..9d190e5 100644 --- a/tests/DotMaybe.CompilerContractTests/PatternMatchingContracts.cs +++ b/tests/DotMaybe.CompilerContractTests/PatternMatchingContracts.cs @@ -48,6 +48,19 @@ public static string Describe(Maybe maybe) ContractAssert.CompilesCleanly(result); } + [Fact] + public void A_type_parameter_pattern_declares_a_variable() + { + // The RC1 compiler rejected `T value` in generic code (CS8780), because T could itself be a union. + // The .NET 11 GA compiler (SDK 11.0.100) accepts it and matches the content, as for a concrete type. + var result = Snippet.Compile(Consumer.File(""" + public static string Describe(Maybe maybe) + where T : notnull => maybe is T value ? $"some {value}" : "none"; + """)); + + ContractAssert.CompilesCleanly(result); + } + [Fact] public void A_concrete_type_pattern_declares_a_variable() { diff --git a/tests/DotMaybe.PropertyTests/Maybes.cs b/tests/DotMaybe.PropertyTests/Maybes.cs index c3c0781..ab69682 100644 --- a/tests/DotMaybe.PropertyTests/Maybes.cs +++ b/tests/DotMaybe.PropertyTests/Maybes.cs @@ -8,9 +8,9 @@ namespace DotMaybe.PropertyTests; /// /// /// Building goes through the union conversions, which are never ambiguous in generic code -/// (e.g. for Maybe<Maybe<int>>). Reading goes through the union members, because C# 15 rejects -/// maybe is T value when T is a type parameter (CS8780, see the compiler contracts). -/// Tests on concrete types read values with pattern matching instead. +/// (e.g. for Maybe<Maybe<int>>). Reading goes through the union members. It predates the .NET 11 +/// GA compiler, which accepts maybe is T value for a type parameter (see the compiler contracts); the RC1 +/// compiler rejected it with CS8780. Tests on concrete types read values with pattern matching. /// internal static class Maybes {