guix_mirror_bot pushed a commit to branch master
in repository guix.
commit b6fc762d4674da5daec049c1eb3496b93c73e7af
Author: Danny Milosavljevic <[email protected]>
AuthorDate: Fri Apr 10 18:01:43 2026 +0200
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
---
gnu/local.mk | 1 +
gnu/packages/dotnet.scm | 1 +
...no-6.12.0-emit-ref-readonly-return-modreq.patch | 254 +++++++++++++++++++++
3 files changed, 256 insertions(+)
diff --git a/gnu/local.mk b/gnu/local.mk
index 6bcf4b22e0..28b67f2969 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1973,6 +1973,7 @@ dist_patch_DATA =
\
%D%/packages/patches/mono-5.8.0-patches.patch \
%D%/packages/patches/mono-5.10.0-later-mcs-changes.patch \
%D%/packages/patches/mono-6.12.0-add-runpath.patch \
+ %D%/packages/patches/mono-6.12.0-emit-ref-readonly-return-modreq.patch
\
%D%/packages/patches/mono-6.12.0-fix-AssemblyResolver.patch \
%D%/packages/patches/mono-6.12.0-fix-ConditionParser.patch \
%D%/packages/patches/mono-mcs-patches-from-5.10.0.patch \
diff --git a/gnu/packages/dotnet.scm b/gnu/packages/dotnet.scm
index ee2bbb9d03..d7e22b8eaa 100644
--- a/gnu/packages/dotnet.scm
+++ b/gnu/packages/dotnet.scm
@@ -1886,6 +1886,7 @@ most of the heavy lifting.")
#$@prepare-mono-source-0))
(patches (search-patches "mono-6.12.0-fix-ConditionParser.patch"
"mono-6.12.0-add-runpath.patch"
+
"mono-6.12.0-emit-ref-readonly-return-modreq.patch"
"mono-6.12.0-fix-AssemblyResolver.patch"))))
(native-inputs (modify-inputs (package-native-inputs mono-5.10.0)
(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 0000000000..f602cfcc81
--- /dev/null
+++ b/gnu/packages/patches/mono-6.12.0-emit-ref-readonly-return-modreq.patch
@@ -0,0 +1,254 @@
+From: Danny Milosavljevic <[email protected]>
+Date: Tue, 26 Aug 2025 00:00:00 +0000
+Subject: [PATCH] mcs: emit InAttribute modreq for ref readonly returns
+
+Why this exists
+
+Upstream Mono 6.12 normally builds the class libraries with the vendored
+Roslyn compiler (`external/roslyn-binaries/Microsoft.Net.Compilers/3.9.0`).
+Guix does not use that path for bootstrap. The bootstrap build configures
+Mono with `--with-csc=mcs`, so `mscorlib.dll` is emitted by the older
+`mcs` compiler instead.
+
+For `ref readonly` returns, old `mcs` already emits the return-parameter
+attribute `System.Runtime.CompilerServices.IsReadOnlyAttribute`, but it omits
+the required return-type custom modifier
+`modreq(System.Runtime.InteropServices.InAttribute)` which Roslyn would
+have done.
+
+What is changed
+
+Patch every old-`mcs` method-signature emission path that can materialize
+source-defined `ref readonly` returns:
+
+ * ordinary methods via `MethodOrOperator.PrepareEmit()`
+ * accessors via `MethodData.DefineMethodBuilder(..., ParametersCompiled)`
+ * generated interface proxy methods via `PendingImplementation.DefineProxy()`
+
+For `ReadOnlyReferenceContainer` returns, these paths now emit the return-type
+custom modifier System.Runtime.InteropServices.InAttribute:
+
+The existing `IsReadOnlyAttribute` return-parameter emission is left alone for
+ordinary methods and accessors. The proxy path did not previously emit it, so
+the patch adds it there too.
+
+Flags and fields involved
+
+`ref readonly` does not live in one place. Mono has to encode it across
+separate method-definition and signature channels:
+
+ * `MethodAttributes flags`
+ Method definition flags such as visibility, `virtual`, `static`, and
+ `final`. These do not encode `ref readonly`.
+ * `CallingConventions`
+ Signature-level calling convention bits such as `hasthis`, `standard`,
+ and `vararg`. These also do not encode `ref readonly`.
+ * `returnTypeRequiredCustomModifiers`
+ Required custom modifiers serialized into the signature blob before the
+ return type. This is where `modreq(InAttribute)` must live.
+ * `returnTypeOptionalCustomModifiers`
+ Optional custom modifiers (`modopt`); unused here, so it remains `null`.
+ * `parameterTypeRequiredCustomModifiers`
+ Required custom modifiers for each parameter. Unchanged here because the
+ problem is on the return type.
+ * `parameterTypeOptionalCustomModifiers`
+ Optional custom modifiers for each parameter. Also unchanged.
+ * `ReturnParameter` custom attributes
+ Attributes attached to parameter slot 0. `mcs` already emits
+ `IsReadOnlyAttribute` there, but that attribute alone does not change the
+ signature blob.
+
+Why the signature fields are separate
+
+Mono and ECMA-335 treat method definition flags, calling convention bits, and
+type custom modifiers as separate parts of the metadata model. `ref readonly`
+return identity depends on the return type itself, so the `modreq` must be
+attached to the return type in the signature blob rather than encoded as a
+method flag.
+
+Runtime and metadata impact
+
+This patch does not add a new VM capability. Mono already stores and encodes
+return custom modifiers in Reflection.Emit and metadata:
+
+ * `mono/metadata/sre.c`
+ * `mono/metadata/sre-encode.c`
+ * `mono/metadata/metadata.c`
+
+However, the modifier is not merely decorative. Mono includes custom
+modifiers in signature equality, and uses that in runtime paths such as method
+lookup, override/interface matching, property accessor matching, verifier
+checks, and AOT/JIT signature caches:
+
+ * `mono/metadata/metadata.c`
+ * `mono/metadata/class-init.c`
+ * `mono/metadata/loader.c`
+ * `mono/metadata/icall.c`
+ * `mono/metadata/metadata-verify.c`
+ * `mono/metadata/verify.c`
+ * `mono/mini/aot-runtime.c`
+ * `mono/mini/mini-runtime.c`
+
+So the direct effect of this patch is to change emitted signature identity for
+`mcs`-built `ref readonly` returns, not just compiler-facing decoration.
+
+Since we are bootstrapping from source anyway, this should be fine.
+Also, the mono 6.12.0 upstream binary would have compiled the libraries
+using Roslyn (which would do this anyway), not mcs--so we diverge a little
+less now.
+
+Reflection and marshaling impact
+
+Managed reflection normally strips custom modifiers from plain reflected
+`Type` objects, but Mono still exposes them through the parameter/return
+modifier icalls used by `RuntimeParameterInfo` and `RuntimePropertyInfo`.
+Return custom modifiers are also inspected by the marshaling layer for
+calling-convention modifiers, although `InAttribute` on returns is not
+special-cased there.
+
+Limitations
+
+This patch does not teach old `mcs` to import `ref readonly` returns from
+metadata. It changes only what old `mcs` emits while Guix bootstraps Mono
+without upstream's vendored Roslyn.
+
+The immediate bootstrap motivation is to make the `mcs`-built `mscorlib.dll`
+expose Roslyn-compatible `ref readonly` return signatures for imported span
+members such as `ReadOnlySpan<T>.this[int]` and
+`ReadOnlySpan<T>.GetPinnableReference()`.
+---
+diff -urN a/mcs/mcs/method.cs b/mcs/mcs/method.cs
+--- a/mcs/mcs/method.cs
++++ b/mcs/mcs/method.cs
+@@ -875,8 +875,15 @@
+ // Generic method has been already defined to resolve
method parameters
+ // correctly when they use type parameters
+ //
+- mb.SetParameters (parameters.GetMetaInfo ());
+- mb.SetReturnType (ReturnType.GetMetaInfo ());
++ var return_type_req_mods =
MethodData.GetReturnTypeRequiredCustomModifiers ();
++ if (return_type_req_mods != null) {
++ mb.SetSignature (
++ ReturnType.GetMetaInfo (),
return_type_req_mods, null,
++ parameters.GetMetaInfo (), null, null);
++ } else {
++ mb.SetParameters (parameters.GetMetaInfo ());
++ mb.SetReturnType (ReturnType.GetMetaInfo ());
++ }
+ }
+
+ public override void WriteDebugSymbol (MonoSymbolFile file)
+@@ -2167,6 +2174,20 @@
+ container.TypeBuilder.DefineMethodOverride (builder,
(MethodInfo) implementing.GetMetaInfo ());
+ }
+
++ public MetaType[] GetReturnTypeRequiredCustomModifiers ()
++ {
++ if (!(method.ReturnType is ReadOnlyReferenceContainer))
++ return null;
++
++ var in_attribute =
member.Module.PredefinedAttributes.In;
++ if (!in_attribute.Define ())
++ return null;
++
++ return new MetaType[] {
++ in_attribute.TypeSpec.GetMetaInfo ()
++ };
++ }
++
+ //
+ // Creates partial MethodBuilder for the method when has
generic parameters used
+ // as arguments or return type
+@@ -2185,9 +2206,17 @@
+ //
+ public MethodBuilder DefineMethodBuilder (TypeDefinition
container, ParametersCompiled param)
+ {
+- DefineMethodBuilder (container);
+- builder.SetReturnType (method.ReturnType.GetMetaInfo
());
+- builder.SetParameters (param.GetMetaInfo ());
++ var return_type_req_mods =
GetReturnTypeRequiredCustomModifiers ();
++ if (return_type_req_mods != null) {
++ builder = container.TypeBuilder.DefineMethod (
++ full_name, flags,
method.CallingConventions,
++ method.ReturnType.GetMetaInfo (),
return_type_req_mods, null,
++ param.GetMetaInfo (), null, null);
++ } else {
++ DefineMethodBuilder (container);
++ builder.SetReturnType
(method.ReturnType.GetMetaInfo ());
++ builder.SetParameters (param.GetMetaInfo ());
++ }
+ return builder;
+ }
+
+@@ -2905,4 +2934,3 @@
+ }
+ }
+ }
+-
+diff -urN a/mcs/mcs/pending.cs b/mcs/mcs/pending.cs
+--- a/mcs/mcs/pending.cs
++++ b/mcs/mcs/pending.cs
+@@ -17,9 +17,11 @@
+ using System.Linq;
+
+ #if STATIC
++using MetaType = IKVM.Reflection.Type;
+ using IKVM.Reflection;
+ using IKVM.Reflection.Emit;
+ #else
++using MetaType = System.Type;
+ using System.Reflection;
+ using System.Reflection.Emit;
+ #endif
+@@ -484,15 +486,42 @@
+
+ var param = iface_method.Parameters;
+
+- MethodBuilder proxy =
container.TypeBuilder.DefineMethod (
+- proxy_name,
+- MethodAttributes.Private |
+- MethodAttributes.HideBySig |
+- MethodAttributes.NewSlot |
+- MethodAttributes.CheckAccessOnOverride |
+- MethodAttributes.Virtual |
MethodAttributes.Final,
+- CallingConventions.Standard |
CallingConventions.HasThis,
+- base_method.ReturnType.GetMetaInfo (),
param.GetMetaInfo ());
++ MetaType[] return_type_req_mods = null;
++ if (base_method.ReturnType is
ReadOnlyReferenceContainer) {
++ var in_attribute =
container.Module.PredefinedAttributes.In;
++ if (in_attribute.Define ()) {
++ return_type_req_mods = new MetaType[] {
++
in_attribute.TypeSpec.GetMetaInfo ()
++ };
++ }
++ }
++
++ MethodBuilder proxy;
++ if (return_type_req_mods != null) {
++ proxy = container.TypeBuilder.DefineMethod (
++ proxy_name,
++ MethodAttributes.Private |
++ MethodAttributes.HideBySig |
++ MethodAttributes.NewSlot |
++ MethodAttributes.CheckAccessOnOverride |
++ MethodAttributes.Virtual |
MethodAttributes.Final,
++ CallingConventions.Standard |
CallingConventions.HasThis,
++ base_method.ReturnType.GetMetaInfo (),
return_type_req_mods, null,
++ param.GetMetaInfo (), null, null);
++ } else {
++ proxy = container.TypeBuilder.DefineMethod (
++ proxy_name,
++ MethodAttributes.Private |
++ MethodAttributes.HideBySig |
++ MethodAttributes.NewSlot |
++ MethodAttributes.CheckAccessOnOverride |
++ MethodAttributes.Virtual |
MethodAttributes.Final,
++ CallingConventions.Standard |
CallingConventions.HasThis,
++ base_method.ReturnType.GetMetaInfo (),
param.GetMetaInfo ());
++ }
++
++ if (base_method.ReturnType is
ReadOnlyReferenceContainer)
++
container.Module.PredefinedAttributes.IsReadOnly.EmitAttribute
(proxy.DefineParameter (0, ParameterAttributes.None, ""));
+
+ if (iface_method.IsGeneric) {
+ var gnames =
iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();