Skip to content

"IL with C#" view: assertion in TransformFieldAndConstructorInitializers when decompiling a static constructor in isolation #3774

Description

@siegfriedpammer

Test case: System.AttributeUsageAttribute (System.Private.CoreLib) — select the type (or its .cctor) with the IL with C# language in a Debug build. An "Assertion Failed" dialog appears.

Assertion failed.
   at ICSharpCode.Decompiler.CSharp.Transforms.TransformFieldAndConstructorInitializers.ConstructorInitializerAnalyzer.MoveFieldInitializersToDeclarations(InitializerSequence sequence, InitializerKind kind) in ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs:line 500
   at ICSharpCode.Decompiler.CSharp.Transforms.TransformFieldAndConstructorInitializers.TransformDeclaration(...)
   at ICSharpCode.Decompiler.CSharp.Transforms.TransformFieldAndConstructorInitializers.Run(...)
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.RunTransforms(...)
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.Decompile(IEnumerable`1 definitions)

Cause: CSharpILMixedLanguage.MixedMethodBodyDisassembler decompiles each method as a bare handle (CSharpDecompiler.Decompile(handle)). For a static constructor, MoveFieldInitializersToDeclarations then finds a static-field initializer statement whose target field has no FieldDeclaration in the partial syntax tree, hits Debug.Assert(kind is InitializerKind.Primary) (kind is Static), and removes the statement — so in Release builds the C# preview of the .cctor silently loses the field assignment.

The regular C# view is not affected because CSharpLanguage.DecompileMethod bundles the type's fields and ctors via CollectFieldsAndCtors before decompiling, so the declaration the initializer moves to is always present.

Standalone repro (Debug build of ICSharpCode.Decompiler; terminates on the failed assert):

var corlib = typeof(object).Assembly.Location;
var module = new PEFile(corlib);
var resolver = new UniversalAssemblyResolver(corlib, false, module.Metadata.DetectTargetFrameworkId());
var metadata = module.Metadata;
var td = metadata.TypeDefinitions.Select(metadata.GetTypeDefinition)
    .Single(t => metadata.GetString(t.Name) == "AttributeUsageAttribute" && metadata.GetString(t.Namespace) == "System");
foreach (var mh in td.GetMethods())
    new CSharpDecompiler(module, resolver, new DecompilerSettings()).Decompile(mh); // asserts on the .cctor

Observed on current master (0f9de78), Linux; not a recent regression — any single-member decompile of a static ctor whose type has static field initializers takes this path.

🤖 Generated with Claude Code

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions