From ee2d7478627fd476e12523c81b408fc9b1e55a90 Mon Sep 17 00:00:00 2001 From: Sebastien Lebreton Date: Sun, 26 Jul 2026 19:28:59 +0200 Subject: [PATCH] Pin the allows ref struct interaction on nullable overrides allows ref struct is inherited implicitly, so restating it on an override is CS0460 even alongside a legal disambiguator. Roslyn still re-emits the byreflike flag on the override's own type parameter, and the general constraint printer turns that flag back into source, so the disambiguator stays legal only as long as it is built separately. Cover a C# 13 base whose annotated and plain methods both allow ref structs. Assisted-by: Copilot:claude-opus-5:GitHub Copilot CLI --- .../TestCases/Pretty/Issue3909.cs | 23 +++++++++++++++++++ .../CSharp/Syntax/TypeSystemAstBuilder.cs | 3 +++ 2 files changed, 26 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3909.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3909.cs index 5bc5a4b75..8d8284974 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3909.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3909.cs @@ -222,5 +222,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return null; } } +#if CS130 + public class BaseWithAllowsRefStruct + { + public virtual void Annotated(T? value) where T : allows ref struct + { + } + + public virtual void Plain(T value) where T : allows ref struct + { + } + } + + public class DerivedWithAllowsRefStruct : BaseWithAllowsRefStruct + { + public override void Annotated(T? value) where T : default + { + } + + public override void Plain(T value) + { + } + } +#endif } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index dc2d2f955..a1e99ea6c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -2619,6 +2619,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax // metadata, so which disambiguator is legal follows from them without resolving the base // member. The restated disambiguator leaves no metadata trace of its own, hence it must be // derived rather than read back. + // The clause is built here rather than through ConvertTypeParameterConstraint, which also + // prints 'allows ref struct' from the byreflike flag. That flag is re-emitted on the + // override's own type parameter as well, and restating it is CS0460. void AddNullabilityDisambiguatingConstraints(MethodDeclaration decl, IMethod method) { if (method.TypeParameters.Count == 0)