[mono][AOT] gsharedvt method calling INumberBase<T>.TryParse fails in aot-only mode

オープン
#137,120 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
38/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
csharp

調査の方向性

Start by tracing Mono full AOT generic sharing for the constrained INumber<T>.TryParse call in BareHolder<T>.Set; the report points to mini-generic-sharing.c and mini_instantiate_gshared_info. Reproduce with the provided iOS AOT setup, comparing the reflection-created closed type with direct construction and interpreter mode. Done when the generic value type parses successfully in AOT-only mode without JIT compilation.

索引モデルが issue の本文から書いたものです。

説明

area-Codegen-AOT-mono untriaged
Description

With Mono full AOT on iOS (UseInterpreter=false), a method on a generic type over a value type that calls T.TryParse(string, NumberStyles, IFormatProvider, out T) fails if the AOT compiler never saw the closed type and it has to run as gsharedvt code. The same method works when the closed type is constructed directly, and with the interpreter.

This came from MudBlazor's slider crashing MAUI Blazor Hybrid apps on iPhone (https://github.com/MudBlazor/MudBlazor/issues/14134). Blazor creates components from a Type and names MudSlider<int> only as a type argument, so the slider always runs as gsharedvt code.

Reproduction Steps

A class library (built with Optimize=true):

using System.Globalization;
using System.Numerics;

public abstract class BareBase
{
    public abstract bool Set(string text);
    public abstract string Text { get; }
}

public sealed class BareHolder<T> : BareBase where T : struct, INumber<T>
{
    private T _value;

    public override string Text => _value.ToString(null, CultureInfo.InvariantCulture);

    public override bool Set(string text)
    {
        if (!T.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
        {
            return false;
        }

        _value = result;
        return true;
    }
}

public static class BareFactory
{
    public static BareBase Create<THolder>() where THolder : BareBase => (BareBase)Activator.CreateInstance(typeof(THolder))!;
}

Called from an iOS app (net10.0-ios, UseInterpreter=false, MtouchLink=SdkOnly):

BareFactory.Create<BareHolder<long>>().Set("181"); // throws
new BareHolder<short>().Set("181");               // works
Expected behavior

Set parses the value, as it does with the interpreter or when the closed type is constructed directly.

Actual behavior
System.ExecutionEngineException: Attempting to JIT compile method 'bool Issue14134.BareHolder`1<long>:Set (string)' while running in aot-only mode.

When the call is inside an async method, the runtime aborts instead:

* Assertion at src/mono/mono/mini/mini-generic-sharing.c:2761, condition `is_ok (error)' not met, function:mini_instantiate_gshared_info, Attempting to JIT compile method 'void Issue14134.SvmAsyncHolder`1/<SetValueTextAsync>d__5<long>:MoveNext ()' while running in aot-only mode.

The reporter's physical iPhone build asserted iface_offset != -1 in mini-generic-sharing.c instead.

Regression?

Unknown.

Known Workarounds
  • Construct the closed type where the AOT compiler sees it, for example new BareHolder<long>() in a method that never runs.
  • Avoid T.TryParse: double.TryParse or decimal.TryParse followed by T.CreateSaturating works in the same gsharedvt context, as do T.Zero, T.One and T.CreateTruncating. MudBlazor does this in https://github.com/MudBlazor/MudBlazor/pull/14136.

Moving T.TryParse into a separate generic method doesn't help. The gsharedvt caller resolves that method on entry and Mono tries to JIT it, even when the call isn't reached.

Configuration

.NET SDK 10.0.403, runtime 10.0.14 (10.0.13 too), iOS workload 27.1.10018, Xcode 27.1, iOS 27.1 arm64 simulator, Debug and Release.

Other information

Possibly related: https://github.com/dotnet/runtime/issues/118697

主要言語
C#
スター
18.4k
フォーク
5.8k
平均マージ
2日 12時間
マージ済み PR(30日)
581

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

dotnet/runtime のほかの issue

dotnet/runtime の issue をすべて見る

似ている issue

C# の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。