diff options
| author | Danny Milosavljevic <dannym@friendly-machines.com> | 2026-04-10 18:01:43 +0200 |
|---|---|---|
| committer | Danny Milosavljevic <dannym@friendly-machines.com> | 2026-07-17 01:41:57 +0200 |
| commit | b6fc762d4674da5daec049c1eb3496b93c73e7af (patch) | |
| tree | f80dc0149f05fcf997e67973379ae9d1e0eaad1e | |
| parent | 8bd02af8349e07a2b93db8451054bf4ab78f56d8 (diff) | |
gnu: mono-bootstrap: Add patch to make Roslyn 3.8 build with fewer patches.
* gnu/packages/patches/mono-6.12.0-emit-ref-readonly-return-modreq.patch: New
file.
* gnu/local.mk (dist_patch_DATA): Add reference to it.
* gnu/packages/dotnet.scm (mono-bootstrap)[source]: Add reference to it.
Change-Id: I3d23ae7868065b509c79bdd094744861dd167a82
| -rw-r--r-- | gnu/local.mk | 1 | ||||
| -rw-r--r-- | gnu/packages/dotnet.scm | 1 | ||||
| -rw-r--r-- | gnu/packages/patches/mono-6.12.0-emit-ref-readonly-return-modreq.patch | 254 |
3 files changed, 256 insertions, 0 deletions
diff --git a/gnu/local.mk b/gnu/local.mk index 6bcf4b22e0b..28b67f29698 100644 --- a/gnu/local.mk +++ b/gnu/local.mk | |||
| @@ -1973,6 +1973,7 @@ dist_patch_DATA = \ | |||
| 1973 | %D%/packages/patches/mono-5.8.0-patches.patch \ | 1973 | %D%/packages/patches/mono-5.8.0-patches.patch \ |
| 1974 | %D%/packages/patches/mono-5.10.0-later-mcs-changes.patch \ | 1974 | %D%/packages/patches/mono-5.10.0-later-mcs-changes.patch \ |
| 1975 | %D%/packages/patches/mono-6.12.0-add-runpath.patch \ | 1975 | %D%/packages/patches/mono-6.12.0-add-runpath.patch \ |
| 1976 | %D%/packages/patches/mono-6.12.0-emit-ref-readonly-return-modreq.patch \ | ||
| 1976 | %D%/packages/patches/mono-6.12.0-fix-AssemblyResolver.patch \ | 1977 | %D%/packages/patches/mono-6.12.0-fix-AssemblyResolver.patch \ |
| 1977 | %D%/packages/patches/mono-6.12.0-fix-ConditionParser.patch \ | 1978 | %D%/packages/patches/mono-6.12.0-fix-ConditionParser.patch \ |
| 1978 | %D%/packages/patches/mono-mcs-patches-from-5.10.0.patch \ | 1979 | %D%/packages/patches/mono-mcs-patches-from-5.10.0.patch \ |
diff --git a/gnu/packages/dotnet.scm b/gnu/packages/dotnet.scm index ee2bbb9d033..d7e22b8eaae 100644 --- a/gnu/packages/dotnet.scm +++ b/gnu/packages/dotnet.scm | |||
| @@ -1886,6 +1886,7 @@ most of the heavy lifting.") | |||
| 1886 | #$@prepare-mono-source-0)) | 1886 | #$@prepare-mono-source-0)) |
| 1887 | (patches (search-patches "mono-6.12.0-fix-ConditionParser.patch" | 1887 | (patches (search-patches "mono-6.12.0-fix-ConditionParser.patch" |
| 1888 | "mono-6.12.0-add-runpath.patch" | 1888 | "mono-6.12.0-add-runpath.patch" |
| 1889 | "mono-6.12.0-emit-ref-readonly-return-modreq.patch" | ||
| 1889 | "mono-6.12.0-fix-AssemblyResolver.patch")))) | 1890 | "mono-6.12.0-fix-AssemblyResolver.patch")))) |
| 1890 | (native-inputs (modify-inputs (package-native-inputs mono-5.10.0) | 1891 | (native-inputs (modify-inputs (package-native-inputs mono-5.10.0) |
| 1891 | (replace "mono" mono-5.10.0))) | 1892 | (replace "mono" mono-5.10.0))) |
diff --git a/gnu/packages/patches/mono-6.12.0-emit-ref-readonly-return-modreq.patch b/gnu/packages/patches/mono-6.12.0-emit-ref-readonly-return-modreq.patch new file mode 100644 index 00000000000..f602cfcc817 --- /dev/null +++ b/gnu/packages/patches/mono-6.12.0-emit-ref-readonly-return-modreq.patch | |||
| @@ -0,0 +1,254 @@ | |||
| 1 | From: Danny Milosavljevic <dannym@friendly-machines.com> | ||
| 2 | Date: Tue, 26 Aug 2025 00:00:00 +0000 | ||
| 3 | Subject: [PATCH] mcs: emit InAttribute modreq for ref readonly returns | ||
| 4 | |||
| 5 | Why this exists | ||
| 6 | |||
| 7 | Upstream Mono 6.12 normally builds the class libraries with the vendored | ||
| 8 | Roslyn compiler (`external/roslyn-binaries/Microsoft.Net.Compilers/3.9.0`). | ||
| 9 | Guix does not use that path for bootstrap. The bootstrap build configures | ||
| 10 | Mono with `--with-csc=mcs`, so `mscorlib.dll` is emitted by the older | ||
| 11 | `mcs` compiler instead. | ||
| 12 | |||
| 13 | For `ref readonly` returns, old `mcs` already emits the return-parameter | ||
| 14 | attribute `System.Runtime.CompilerServices.IsReadOnlyAttribute`, but it omits | ||
| 15 | the required return-type custom modifier | ||
| 16 | `modreq(System.Runtime.InteropServices.InAttribute)` which Roslyn would | ||
| 17 | have done. | ||
| 18 | |||
| 19 | What is changed | ||
| 20 | |||
| 21 | Patch every old-`mcs` method-signature emission path that can materialize | ||
| 22 | source-defined `ref readonly` returns: | ||
| 23 | |||
| 24 | * ordinary methods via `MethodOrOperator.PrepareEmit()` | ||
| 25 | * accessors via `MethodData.DefineMethodBuilder(..., ParametersCompiled)` | ||
| 26 | * generated interface proxy methods via `PendingImplementation.DefineProxy()` | ||
| 27 | |||
| 28 | For `ReadOnlyReferenceContainer` returns, these paths now emit the return-type | ||
| 29 | custom modifier System.Runtime.InteropServices.InAttribute: | ||
| 30 | |||
| 31 | The existing `IsReadOnlyAttribute` return-parameter emission is left alone for | ||
| 32 | ordinary methods and accessors. The proxy path did not previously emit it, so | ||
| 33 | the patch adds it there too. | ||
| 34 | |||
| 35 | Flags and fields involved | ||
| 36 | |||
| 37 | `ref readonly` does not live in one place. Mono has to encode it across | ||
| 38 | separate method-definition and signature channels: | ||
| 39 | |||
| 40 | * `MethodAttributes flags` | ||
| 41 | Method definition flags such as visibility, `virtual`, `static`, and | ||
| 42 | `final`. These do not encode `ref readonly`. | ||
| 43 | * `CallingConventions` | ||
| 44 | Signature-level calling convention bits such as `hasthis`, `standard`, | ||
| 45 | and `vararg`. These also do not encode `ref readonly`. | ||
| 46 | * `returnTypeRequiredCustomModifiers` | ||
| 47 | Required custom modifiers serialized into the signature blob before the | ||
| 48 | return type. This is where `modreq(InAttribute)` must live. | ||
| 49 | * `returnTypeOptionalCustomModifiers` | ||
| 50 | Optional custom modifiers (`modopt`); unused here, so it remains `null`. | ||
| 51 | * `parameterTypeRequiredCustomModifiers` | ||
| 52 | Required custom modifiers for each parameter. Unchanged here because the | ||
| 53 | problem is on the return type. | ||
| 54 | * `parameterTypeOptionalCustomModifiers` | ||
| 55 | Optional custom modifiers for each parameter. Also unchanged. | ||
| 56 | * `ReturnParameter` custom attributes | ||
| 57 | Attributes attached to parameter slot 0. `mcs` already emits | ||
| 58 | `IsReadOnlyAttribute` there, but that attribute alone does not change the | ||
| 59 | signature blob. | ||
| 60 | |||
| 61 | Why the signature fields are separate | ||
| 62 | |||
| 63 | Mono and ECMA-335 treat method definition flags, calling convention bits, and | ||
| 64 | type custom modifiers as separate parts of the metadata model. `ref readonly` | ||
| 65 | return identity depends on the return type itself, so the `modreq` must be | ||
| 66 | attached to the return type in the signature blob rather than encoded as a | ||
| 67 | method flag. | ||
| 68 | |||
| 69 | Runtime and metadata impact | ||
| 70 | |||
| 71 | This patch does not add a new VM capability. Mono already stores and encodes | ||
| 72 | return custom modifiers in Reflection.Emit and metadata: | ||
| 73 | |||
| 74 | * `mono/metadata/sre.c` | ||
| 75 | * `mono/metadata/sre-encode.c` | ||
| 76 | * `mono/metadata/metadata.c` | ||
| 77 | |||
| 78 | However, the modifier is not merely decorative. Mono includes custom | ||
| 79 | modifiers in signature equality, and uses that in runtime paths such as method | ||
| 80 | lookup, override/interface matching, property accessor matching, verifier | ||
| 81 | checks, and AOT/JIT signature caches: | ||
| 82 | |||
| 83 | * `mono/metadata/metadata.c` | ||
| 84 | * `mono/metadata/class-init.c` | ||
| 85 | * `mono/metadata/loader.c` | ||
| 86 | * `mono/metadata/icall.c` | ||
| 87 | * `mono/metadata/metadata-verify.c` | ||
| 88 | * `mono/metadata/verify.c` | ||
| 89 | * `mono/mini/aot-runtime.c` | ||
| 90 | * `mono/mini/mini-runtime.c` | ||
| 91 | |||
| 92 | So the direct effect of this patch is to change emitted signature identity for | ||
| 93 | `mcs`-built `ref readonly` returns, not just compiler-facing decoration. | ||
| 94 | |||
| 95 | Since we are bootstrapping from source anyway, this should be fine. | ||
| 96 | Also, the mono 6.12.0 upstream binary would have compiled the libraries | ||
| 97 | using Roslyn (which would do this anyway), not mcs--so we diverge a little | ||
| 98 | less now. | ||
| 99 | |||
| 100 | Reflection and marshaling impact | ||
| 101 | |||
| 102 | Managed reflection normally strips custom modifiers from plain reflected | ||
| 103 | `Type` objects, but Mono still exposes them through the parameter/return | ||
| 104 | modifier icalls used by `RuntimeParameterInfo` and `RuntimePropertyInfo`. | ||
| 105 | Return custom modifiers are also inspected by the marshaling layer for | ||
| 106 | calling-convention modifiers, although `InAttribute` on returns is not | ||
| 107 | special-cased there. | ||
| 108 | |||
| 109 | Limitations | ||
| 110 | |||
| 111 | This patch does not teach old `mcs` to import `ref readonly` returns from | ||
| 112 | metadata. It changes only what old `mcs` emits while Guix bootstraps Mono | ||
| 113 | without upstream's vendored Roslyn. | ||
| 114 | |||
| 115 | The immediate bootstrap motivation is to make the `mcs`-built `mscorlib.dll` | ||
| 116 | expose Roslyn-compatible `ref readonly` return signatures for imported span | ||
| 117 | members such as `ReadOnlySpan<T>.this[int]` and | ||
| 118 | `ReadOnlySpan<T>.GetPinnableReference()`. | ||
| 119 | --- | ||
| 120 | diff -urN a/mcs/mcs/method.cs b/mcs/mcs/method.cs | ||
| 121 | --- a/mcs/mcs/method.cs | ||
| 122 | +++ b/mcs/mcs/method.cs | ||
| 123 | @@ -875,8 +875,15 @@ | ||
| 124 | // Generic method has been already defined to resolve method parameters | ||
| 125 | // correctly when they use type parameters | ||
| 126 | // | ||
| 127 | - mb.SetParameters (parameters.GetMetaInfo ()); | ||
| 128 | - mb.SetReturnType (ReturnType.GetMetaInfo ()); | ||
| 129 | + var return_type_req_mods = MethodData.GetReturnTypeRequiredCustomModifiers (); | ||
| 130 | + if (return_type_req_mods != null) { | ||
| 131 | + mb.SetSignature ( | ||
| 132 | + ReturnType.GetMetaInfo (), return_type_req_mods, null, | ||
| 133 | + parameters.GetMetaInfo (), null, null); | ||
| 134 | + } else { | ||
| 135 | + mb.SetParameters (parameters.GetMetaInfo ()); | ||
| 136 | + mb.SetReturnType (ReturnType.GetMetaInfo ()); | ||
| 137 | + } | ||
| 138 | } | ||
| 139 | |||
| 140 | public override void WriteDebugSymbol (MonoSymbolFile file) | ||
| 141 | @@ -2167,6 +2174,20 @@ | ||
| 142 | container.TypeBuilder.DefineMethodOverride (builder, (MethodInfo) implementing.GetMetaInfo ()); | ||
| 143 | } | ||
| 144 | |||
| 145 | + public MetaType[] GetReturnTypeRequiredCustomModifiers () | ||
| 146 | + { | ||
| 147 | + if (!(method.ReturnType is ReadOnlyReferenceContainer)) | ||
| 148 | + return null; | ||
| 149 | + | ||
| 150 | + var in_attribute = member.Module.PredefinedAttributes.In; | ||
| 151 | + if (!in_attribute.Define ()) | ||
| 152 | + return null; | ||
| 153 | + | ||
| 154 | + return new MetaType[] { | ||
| 155 | + in_attribute.TypeSpec.GetMetaInfo () | ||
| 156 | + }; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | // | ||
| 160 | // Creates partial MethodBuilder for the method when has generic parameters used | ||
| 161 | // as arguments or return type | ||
| 162 | @@ -2185,9 +2206,17 @@ | ||
| 163 | // | ||
| 164 | public MethodBuilder DefineMethodBuilder (TypeDefinition container, ParametersCompiled param) | ||
| 165 | { | ||
| 166 | - DefineMethodBuilder (container); | ||
| 167 | - builder.SetReturnType (method.ReturnType.GetMetaInfo ()); | ||
| 168 | - builder.SetParameters (param.GetMetaInfo ()); | ||
| 169 | + var return_type_req_mods = GetReturnTypeRequiredCustomModifiers (); | ||
| 170 | + if (return_type_req_mods != null) { | ||
| 171 | + builder = container.TypeBuilder.DefineMethod ( | ||
| 172 | + full_name, flags, method.CallingConventions, | ||
| 173 | + method.ReturnType.GetMetaInfo (), return_type_req_mods, null, | ||
| 174 | + param.GetMetaInfo (), null, null); | ||
| 175 | + } else { | ||
| 176 | + DefineMethodBuilder (container); | ||
| 177 | + builder.SetReturnType (method.ReturnType.GetMetaInfo ()); | ||
| 178 | + builder.SetParameters (param.GetMetaInfo ()); | ||
| 179 | + } | ||
| 180 | return builder; | ||
| 181 | } | ||
| 182 | |||
| 183 | @@ -2905,4 +2934,3 @@ | ||
| 184 | } | ||
| 185 | } | ||
| 186 | } | ||
| 187 | - | ||
| 188 | diff -urN a/mcs/mcs/pending.cs b/mcs/mcs/pending.cs | ||
| 189 | --- a/mcs/mcs/pending.cs | ||
| 190 | +++ b/mcs/mcs/pending.cs | ||
| 191 | @@ -17,9 +17,11 @@ | ||
| 192 | using System.Linq; | ||
| 193 | |||
| 194 | #if STATIC | ||
| 195 | +using MetaType = IKVM.Reflection.Type; | ||
| 196 | using IKVM.Reflection; | ||
| 197 | using IKVM.Reflection.Emit; | ||
| 198 | #else | ||
| 199 | +using MetaType = System.Type; | ||
| 200 | using System.Reflection; | ||
| 201 | using System.Reflection.Emit; | ||
| 202 | #endif | ||
| 203 | @@ -484,15 +486,42 @@ | ||
| 204 | |||
| 205 | var param = iface_method.Parameters; | ||
| 206 | |||
| 207 | - MethodBuilder proxy = container.TypeBuilder.DefineMethod ( | ||
| 208 | - proxy_name, | ||
| 209 | - MethodAttributes.Private | | ||
| 210 | - MethodAttributes.HideBySig | | ||
| 211 | - MethodAttributes.NewSlot | | ||
| 212 | - MethodAttributes.CheckAccessOnOverride | | ||
| 213 | - MethodAttributes.Virtual | MethodAttributes.Final, | ||
| 214 | - CallingConventions.Standard | CallingConventions.HasThis, | ||
| 215 | - base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ()); | ||
| 216 | + MetaType[] return_type_req_mods = null; | ||
| 217 | + if (base_method.ReturnType is ReadOnlyReferenceContainer) { | ||
| 218 | + var in_attribute = container.Module.PredefinedAttributes.In; | ||
| 219 | + if (in_attribute.Define ()) { | ||
| 220 | + return_type_req_mods = new MetaType[] { | ||
| 221 | + in_attribute.TypeSpec.GetMetaInfo () | ||
| 222 | + }; | ||
| 223 | + } | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + MethodBuilder proxy; | ||
| 227 | + if (return_type_req_mods != null) { | ||
| 228 | + proxy = container.TypeBuilder.DefineMethod ( | ||
| 229 | + proxy_name, | ||
| 230 | + MethodAttributes.Private | | ||
| 231 | + MethodAttributes.HideBySig | | ||
| 232 | + MethodAttributes.NewSlot | | ||
| 233 | + MethodAttributes.CheckAccessOnOverride | | ||
| 234 | + MethodAttributes.Virtual | MethodAttributes.Final, | ||
| 235 | + CallingConventions.Standard | CallingConventions.HasThis, | ||
| 236 | + base_method.ReturnType.GetMetaInfo (), return_type_req_mods, null, | ||
| 237 | + param.GetMetaInfo (), null, null); | ||
| 238 | + } else { | ||
| 239 | + proxy = container.TypeBuilder.DefineMethod ( | ||
| 240 | + proxy_name, | ||
| 241 | + MethodAttributes.Private | | ||
| 242 | + MethodAttributes.HideBySig | | ||
| 243 | + MethodAttributes.NewSlot | | ||
| 244 | + MethodAttributes.CheckAccessOnOverride | | ||
| 245 | + MethodAttributes.Virtual | MethodAttributes.Final, | ||
| 246 | + CallingConventions.Standard | CallingConventions.HasThis, | ||
| 247 | + base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ()); | ||
| 248 | + } | ||
| 249 | + | ||
| 250 | + if (base_method.ReturnType is ReadOnlyReferenceContainer) | ||
| 251 | + container.Module.PredefinedAttributes.IsReadOnly.EmitAttribute (proxy.DefineParameter (0, ParameterAttributes.None, "")); | ||
| 252 | |||
| 253 | if (iface_method.IsGeneric) { | ||
| 254 | var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray (); | ||
