Skip to content

TuiConfigurationBuilder.ConvertValue breaks NativeAOT/trim: unannotated TypeDescriptor.GetConverter (IL2026/IL3050) — regression from #5411 #5561

Description

@tig

Summary

TuiConfigurationBuilder.ConvertValue(string, Type) calls TypeDescriptor.GetConverter(Type) — which is annotated [RequiresUnreferencedCode]/[RequiresDynamicCode] — without any trim/AOT annotation or suppression. As a result, any NativeAOT (or trimmed) application that consumes TuiConfigurationBuilder fails to publish with IL2026 (and would hit IL3050). This is a regression introduced by #5411 (the CM→MEC migration): the sibling binding methods in the same file were annotated, but ConvertValue was missed.

Terminal.Gui advertises AOT/trim compatibility, so a published API on the primary configuration entry point breaking AOT consumers is a correctness gap.

Affected version

2.4.15 (stable) and the release/v2.4.14-beta.1 line — i.e. everything containing #5411. TuiConfigurationBuilder did not exist before #5411, so this ships in the first release that has it.

Repro

Any project with true that calls new TuiConfigurationBuilder(...) and binds settings, then:

dotnet publish .csproj -r osx-arm64 -c Release   # or linux-x64 / win-x64

Concretely, tui-cs/Editor's examples/ted (NativeAOT) fails on all three RIDs. Real CI evidence:

Error (identical across osx-arm64 / linux-x64 / win-x64)

ILC : Trim analysis error IL2026: Terminal.Gui.Configuration.TuiConfigurationBuilder.ConvertValue(String,Type):
  Using member 'System.ComponentModel.TypeDescriptor.GetConverter(Type)' which has 'RequiresUnreferencedCodeAttribute'
  can break functionality when trimming application code. Generic TypeConverters may require the generic types to be
  annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.
.../Terminal.Gui/2.4.15/lib/net10.0/Terminal.Gui.dll : error IL2104: Assembly 'Terminal.Gui' produced trim warnings.
ILC : ... exited with return value 0, but errors were detected during execution.

Root cause

In Terminal.Gui/Configuration/Settings/TuiConfigurationBuilder.cs (as of v2.4.15):

  • ConvertValue (~line 250) has a fast path for string/bool/int/Rune/enum, then falls back to TypeDescriptor.GetConverter(targetType) (~line 277) for any other type.
  • ConvertValue carries no [UnconditionalSuppressMessage] / [RequiresUnreferencedCode] / [RequiresDynamicCode] attribute.
  • By contrast, the sibling methods in the same file (~lines 158, 169, 211 — e.g. BindFlatDottedKeys, which is the caller) are annotated:
    [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Settings POCOs are simple types preserved by DynamicDependency in ConfigPropertyHostTypes.")]
    [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Settings POCOs are simple types; no generic instantiation needed at runtime.")]
    The caller's suppression does not silence the warning originating inside ConvertValue, so ILC reports it at the ConvertValue → GetConverter call site.

Suggested fix

Either:

  1. Annotate ConvertValue consistently with the same [UnconditionalSuppressMessage("Trimming","IL2026")] + [UnconditionalSuppressMessage("AOT","IL3050")] (and matching justification) used on the sibling methods — if the "settings POCOs are simple types preserved by DynamicDependency" guarantee genuinely holds for the TypeConverter fallback path; or
  2. Make the fallback trim-safe — e.g. drop the TypeDescriptor.GetConverter fallback (the scalar fast path already covers the common cases) and require non-scalar settings types to be handled explicitly, so the public binding path has no RequiresUnreferencedCode dependency at all.

Option 2 is the stronger guarantee for a public, AOT-advertised API; option 1 restores parity with the rest of the file as a minimal fix.

Impact

Blocks NativeAOT/trimmed downstream consumers (e.g. the tui-cs/Editor ted app and its release pipeline) from publishing against any #5411-containing Terminal.Gui.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions