140 Commits
Author SHA1 Message Date
Daniel Grunwald fa863a8229 #4019: transform value.<Clone>$() into value with {} even if there are no initializers.
This commit also changes the ILAst representation of with-initializers to preserve the `<Clone>$()` call, so that BlockKind.WithInitializer only forces the block into a certain shape, without assigning its special semantics.
2026-10-11 14:05:15 +02:00
Daniel Grunwald 6fcb387ffb Simplify logic.not in patterns 2026-09-13 20:57:42 +02:00
Daniel Grunwald 42d70f74e2 Do not transforms pattern further as if they were real instructions. 2026-09-13 19:52:36 +02:00
Daniel Grunwald cea8ff2dbb Use type bool for logic.and/logic.or. 2026-09-13 15:22:41 +02:00
Daniel Grunwald c760a1d622 Fix "Remove redundant comp(... != 0)" to avoid changing the value
When an IfInstruction was created with resultType=bool, because only
values 0 or 1 are possible, then we need to preserve this property in
transforms -- otherwise the added correctness test would fail.
2026-09-13 11:57:46 +02:00
Daniel Grunwald fb9ff796da Fix stackalloc char[4] turning to stackalloc short[4]. 2026-09-06 20:13:54 +02:00
Siegfried Pammer 5347b03ade Keep the localloc pointer in a stackalloc initializer block
The block addresses its allocation through the pointer localloc returns, and
only its result is a Span<T>. Retyping the initializer variable to the span
made every element store ask for a pointer it no longer had, which was papered
over with a conv from the span; once Obj and VT became distinct stack types
that conv had no conversion kind left and the whole method failed to decompile.

The span constructor becomes the block's final instruction instead, so the
element stores keep the pointer they were written against and the block still
evaluates to the span.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-09-05 22:11:12 +02:00
Daniel Grunwald 60efa214b9 NullCoalescingInstruction: use a more detailed invariant.
Compute the underlying result type instead of storing it explicitly.
2026-09-05 22:11:12 +02:00
Daniel Grunwald c4e181505c Prepare for a split of StackType.O into StackType.Obj and StackType.VT: the easy cases 2026-09-05 22:11:12 +02:00
Daniel Grunwald 76f67f3fca Add DetectImprovedTypeForForBitOp as a transform step.
Previously this happened within the InferType implementation.
2026-08-30 21:27:50 +02:00
Daniel Grunwald 5526400b17 Extend InferType() to every ILInstruction
Change InferType() to an abstract method and implement for every ILInstruction.
With this change, we now always have enough information to create a variable of an appropriate type to store the result of evaluating the instruction.
This previously was not the case for instructions producing "other value type", for which the stacktype-based fallback incorrectly produced `object`.
2026-08-30 21:25:45 +02:00
Siegfried Pammer 9ea8d7c6ac Say why ref results are excluded from switch expressions
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-08-08 14:59:58 +02:00
Sebastien LebretonandCopilot 076a43671e Keep ref-returning switches as statements
C# has no ref-returning switch expression. Skip the transform for StackType.Ref and cover the statement form in RefLocalsAndReturns.

Co-authored-by: Copilot <[email protected]>
Copilot-Session: 0dd407b6-9410-48df-add5-761ca4a8dec0
2026-08-08 14:59:58 +02:00
Siegfried Pammer 322add8d14 Fix dynamic event-assignment decompilation leaking is-event opcode
DynamicIsEventInstruction has no ExpressionBuilder visitor, so any is-event
diamond that survives the transform pipeline leaks as an "OpCode not supported"
comment. The collapse only fired for the plain statement form; every other
lowering the compiler emits for "d.Event += b" was missed:

- a copy-of-value temporary between the getmember cache and the diamond (modern
  Roslyn emits it for the statement form),
- the result-used shape, where the diamond is a value-if nested inside the
  consuming expression (a call argument, or a leave's value when returned),
- the result-returned shape as two leaves, "if (isevent) leave(add)" plus a
  fall-through "leave(compound)" rather than an if/else, and
- the same two-leaves shape when the optimizer drops the getmember cache (legacy
  csc and Roslyn <= 2.x): the is-event flag then feeds a single branch, inlining
  folds it into the condition, and the compound leave is already collapsed, so
  the cache-based entry never matched it and the branch leaked. These compilers
  only run on Windows CI, so the gap was invisible on Linux.

The descendants walk handles the nested value-if once an over-strict
copy-of-value bounds guard (which never held for the compact return block) is
dropped. The two two-leaves shapes share a skeleton matcher and the add/remove
accessor-name check, and both validate the accessor name and arguments. Re-running
from the collapsed statement instead of the next one avoids indexing past the end
of a block that has shrunk to a single leave.

Assisted-by: Claude:claude-opus-4-8:Claude Code
2026-07-11 12:39:03 +02:00
Siegfried Pammer 04505bbdf2 Highlight and scroll to the changed instruction in the ILAst view
The C# debug-steps view highlights and centers the exact AST node a
transform changed; the ILAst view already had the step tree and
replay-at-step but produced no highlight. Bring it to parity.

IL rendering has no token-writer seam like the C# output visitor, so
per-instruction text spans are recorded by bracketing
ILInstruction.WriteTo via a new INodeTrackingOutput. The dominant
inst.ReplaceWith(newInst) transform pattern detaches the instruction
passed to Step, so ILTransformContext gains EndStep to record the
produced instruction; Stepper additionally records the position's
ancestor chain as fallback candidates before the step-limit throw, so
the "show state before" view -- which halts at the selected step --
still resolves to a surviving ancestor (ultimately the ILFunction).
The highlight-range resolver is shared with the C# language.

Assisted-by: Claude:claude-opus-4-8:Claude Code
2026-07-02 01:08:51 +02:00
Siegfried Pammer 49368caf1c Rename option to "UseObjectCreationOfGenericTypeParameter" 2025-07-12 14:24:08 +02:00
Siegfried Pammer 2461f520ab Move LocalFunctionDecompiler.GetStatement to Block.GetContainingStatement 2025-07-04 19:08:07 +02:00
Siegfried Pammer 47dd905ad0 Make InlineArrayTransform post-order 2025-06-15 17:22:07 +02:00
Siegfried Pammer 79f7a188b0 Add support for C# 12 inline array expressions 2025-06-15 17:22:07 +02:00
Siegfried Pammer 6c72d1c5f0 Add transform to remove unconstrained generic reference type check. 2025-04-07 23:10:06 +02:00
Siegfried Pammer 9f77f8a919 Prevent inlining of call arguments when doing so would change order of evaluation with regards to the implicit ldobj performed by a constrained.callvirt. 2025-04-06 00:41:12 +02:00
ds5678 b0d6fa2276 Add support for array initialization based on RuntimeHelpers.CreateSpan<T> 2025-02-22 11:12:49 +01:00
Siegfried Pammer 2ca5b5affe Rewrite AssignVariableNames algorithm to use variable usages instead of the list of variables. 2024-07-27 13:35:20 +02:00
Siegfried Pammer 28aa88bfb8 Make NullableLiftingTransform handle the case where ExpressionTransforms.VisitComp already lifted a nullable comparison with constant. 2024-07-20 11:58:00 +02:00
Siegfried Pammer 9d22e3ab41 NullableLiftingTransform: Undo new compiler optimization (omitting get_HasValue for comparisions with constants) 2024-07-20 10:40:31 +02:00
Siegfried Pammer 9ba47db69b Add new a.GetValueOrDefault(b) -> a ?? b transform for side-effect-free default values. 2024-03-29 21:14:18 +01:00
Siegfried Pammer e193b838da Move TransformDecimalCtorToConstant to EarlyExpressionTransforms 2023-08-05 15:33:55 +02:00
Siegfried Pammer 688474facd Add missing DecompilerSettings for new language features 2023-08-05 14:33:47 +02:00
Daniel Grunwald f568123704 Add support for UTF8 string literals 2023-05-30 11:20:20 +02:00
ElektroKill 01fd6e97f3 Fixed overflow exception and added tests 2023-04-07 12:20:07 +02:00
ElektroKill bf0fe3aa15 Consider constructor type when lifting decimal constants 2023-04-06 22:00:10 +02:00
Daniel Grunwald d3919aa99a Support native ints in "Combine bit.and into shift" transform 2022-11-10 21:16:14 +01:00
Siegfried Pammer dbbcbb87fe Make pattern matching transform a simple ILTransform for both reference and value types. Check that the true branch dominates all uses of the pattern variable. 2021-09-05 18:46:03 +02:00
Siegfried Pammer cd0c76d7b1 Support leave instructions in RemoveInfeasiblePathTransform and transform match(x) ? true : false to match(x). 2021-07-25 15:52:37 +02:00
Siegfried Pammer 76227af89d Fix #1749, fix #2339, fix #2353: Add support for rethrow in async exception handlers, fix await catch/finally patterns for complex methods. 2021-04-07 13:12:49 +02:00
Daniel Grunwald 80cf72e498 #2182: Move "Transform call to struct constructor" from EarlyExpressionTransforms to ILReader.
This fixes the evaluation order for such calls (and an assertion in StObj.CheckTargetSlot).
2020-10-07 23:59:22 +02:00
dotnet format 0d9f871a4f #2128: Reformat the whole code base. 2020-08-28 01:05:38 +02:00
Siegfried Pammer 7ed4a736f3 Fix #2103: Support for Stackalloc initializers not working for byte 2020-08-22 23:00:48 +02:00
Daniel Grunwald abb9d49a0f Add support for C# 8 switch expressions. 2020-08-02 11:57:33 +02:00
Siegfried Pammer 2e5a597300 Merge branch 'master' into ranges 2020-05-01 18:55:32 +02:00
Siegfried Pammer 9c8df1d949 Fix assignment of ILRanges in ExpressionTransforms.TransformCatchVariable 2020-04-29 21:14:09 +02:00
Daniel Grunwald dc6e094a30 Add support for indexing arrays using System.Index 2020-04-19 08:26:18 +02:00
Siegfried Pammer 4db41f69db Fix #1919: Use unmapped IL offsets at the start of a catch-block for the 'exception specifier' sequence point. 2020-04-08 12:13:21 +02:00
Siegfried Pammer ba5c645257 Add TryCatchHandler annotation to CatchClause 2020-04-08 09:56:27 +02:00
SilverFox 5914d5b96b Fix #1854 2019-12-09 18:26:03 +08:00
Daniel Grunwald 93806b46fa Fix compound assignments with type bool.
This required removing the "Replace bit.and with logic.and" transform, as it interfered with the compound assignment transform.
2019-11-30 23:19:18 +01:00
Daniel Grunwald a1c211f326 Fix #1794: Avoid extra cast to double for ulong -> float conversion 2019-11-08 23:16:51 +01:00
Daniel Grunwald f1021cb300 Fix #1643: Handle unbox.any in TransformCatchVariable. 2019-08-17 23:54:49 +02:00
Siegfried Pammer 03151bf37b Remove redundant code in ExpressionTransforms.VisitBlockContainer 2019-08-17 23:11:07 +02:00
Siegfried Pammer 9c06d5c924 Fix #1642: Allow StackTypes I, I4, I8 and Unknown as switch value, convert Unknown and I to I8. 2019-08-17 20:05:08 +02:00