summaryrefslogtreecommitdiff
path: root/gnu
diff options
context:
space:
mode:
authorDanny Milosavljevic <dannym@friendly-machines.com>2026-04-10 18:01:43 +0200
committerDanny Milosavljevic <dannym@friendly-machines.com>2026-07-17 01:41:57 +0200
commitb6fc762d4674da5daec049c1eb3496b93c73e7af (patch)
treef80dc0149f05fcf997e67973379ae9d1e0eaad1e /gnu
parent8bd02af8349e07a2b93db8451054bf4ab78f56d8 (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
Diffstat (limited to 'gnu')
-rw-r--r--gnu/local.mk1
-rw-r--r--gnu/packages/dotnet.scm1
-rw-r--r--gnu/packages/patches/mono-6.12.0-emit-ref-readonly-return-modreq.patch254
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 @@
1From: Danny Milosavljevic <dannym@friendly-machines.com>
2Date: Tue, 26 Aug 2025 00:00:00 +0000
3Subject: [PATCH] mcs: emit InAttribute modreq for ref readonly returns
4
5Why this exists
6
7Upstream Mono 6.12 normally builds the class libraries with the vendored
8Roslyn compiler (`external/roslyn-binaries/Microsoft.Net.Compilers/3.9.0`).
9Guix does not use that path for bootstrap. The bootstrap build configures
10Mono with `--with-csc=mcs`, so `mscorlib.dll` is emitted by the older
11`mcs` compiler instead.
12
13For `ref readonly` returns, old `mcs` already emits the return-parameter
14attribute `System.Runtime.CompilerServices.IsReadOnlyAttribute`, but it omits
15the required return-type custom modifier
16`modreq(System.Runtime.InteropServices.InAttribute)` which Roslyn would
17have done.
18
19What is changed
20
21Patch every old-`mcs` method-signature emission path that can materialize
22source-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
28For `ReadOnlyReferenceContainer` returns, these paths now emit the return-type
29custom modifier System.Runtime.InteropServices.InAttribute:
30
31The existing `IsReadOnlyAttribute` return-parameter emission is left alone for
32ordinary methods and accessors. The proxy path did not previously emit it, so
33the patch adds it there too.
34
35Flags and fields involved
36
37`ref readonly` does not live in one place. Mono has to encode it across
38separate 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
61Why the signature fields are separate
62
63Mono and ECMA-335 treat method definition flags, calling convention bits, and
64type custom modifiers as separate parts of the metadata model. `ref readonly`
65return identity depends on the return type itself, so the `modreq` must be
66attached to the return type in the signature blob rather than encoded as a
67method flag.
68
69Runtime and metadata impact
70
71This patch does not add a new VM capability. Mono already stores and encodes
72return 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
78However, the modifier is not merely decorative. Mono includes custom
79modifiers in signature equality, and uses that in runtime paths such as method
80lookup, override/interface matching, property accessor matching, verifier
81checks, 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
92So 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
95Since we are bootstrapping from source anyway, this should be fine.
96Also, the mono 6.12.0 upstream binary would have compiled the libraries
97using Roslyn (which would do this anyway), not mcs--so we diverge a little
98less now.
99
100Reflection and marshaling impact
101
102Managed reflection normally strips custom modifiers from plain reflected
103`Type` objects, but Mono still exposes them through the parameter/return
104modifier icalls used by `RuntimeParameterInfo` and `RuntimePropertyInfo`.
105Return custom modifiers are also inspected by the marshaling layer for
106calling-convention modifiers, although `InAttribute` on returns is not
107special-cased there.
108
109Limitations
110
111This patch does not teach old `mcs` to import `ref readonly` returns from
112metadata. It changes only what old `mcs` emits while Guix bootstraps Mono
113without upstream's vendored Roslyn.
114
115The immediate bootstrap motivation is to make the `mcs`-built `mscorlib.dll`
116expose Roslyn-compatible `ref readonly` return signatures for imported span
117members such as `ReadOnlySpan<T>.this[int]` and
118`ReadOnlySpan<T>.GetPinnableReference()`.
119---
120diff -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-
188diff -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 ();