Fix #3820: decompile dynamic ~ as ~x instead of an unsupported-opcode error - #3822
Merged
siegfriedpammer merged 4 commits intoJun 26, 2026
Merged
Conversation
…rted-opcode error VisitDynamicUnaryOperatorInstruction handled every dynamic unary operator except ExpressionType.OnesComplement, so ~x on a dynamic operand fell through to the unsupported-opcode error expression and produced uncompilable output (an incomplete cast that fails to parse). Map it to the bitwise-complement operator, like the sibling unary cases. Assisted-by: Copilot:claude-opus-4.8:GitHub Copilot CLI
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes decompilation of the unary ~ operator when applied to a dynamic operand by adding the missing ExpressionType.OnesComplement case in the dynamic unary operator visitor, so the decompiler emits compilable C# (~x) instead of the unsupported-opcode placeholder.
Changes:
- Map
ExpressionType.OnesComplementtoUnaryOperatorType.BitNotinVisitDynamicUnaryOperatorInstruction. - Extend the
Pretty/DynamicTestsUnaryOperatorstest case to include~aalongside existing-aand+acoverage.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | Adds dynamic OnesComplement handling by emitting a BitNot unary operator expression. |
| ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs | Adds ~a usage to ensure the pretty-printed dynamic unary operator set includes bitwise-not. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The correctness DynamicTests only ran binary + - * / on a dynamic operand, so no test recompiled and executed decompiled output for any unary operator. That gap is why ~ on a dynamic value (issue icsharpcode#3820) shipped uncompilable output undetected: a correctness case round-trips the decompilation through the compiler, so it fails the moment the decompiler emits something that does not recompile. Add ~, -, +, and ! cases. They pin the runtime semantics of the dynamic unary path and would have caught the OnesComplement regression directly. Assisted-by: Claude:claude-opus-4-8:Claude Code
The pretty DynamicTests only exercised += -= *= /= on a dynamic target, leaving %= &= |= ^= <<= >>= unverified even though VisitDynamicCompoundAssign and GetAssignmentOperatorTypeFromExpressionType already map them. Add them so the full set of dynamic compound-assignment operators is pinned by a round-trip test. Assisted-by: Claude:claude-opus-4-8:Claude Code
The pretty DynamicTests exercised arithmetic and relational binary operators on dynamic operands, but never the bitwise and shift operators (& | ^ << >>). Add a BitwiseAndShiftBinaryOperators case so this operator family is pinned by a round-trip test alongside the others. Assisted-by: Claude:claude-opus-4-8:Claude Code
siegfriedpammer
self-requested a review
June 26, 2026 10:42
siegfriedpammer
approved these changes
Jun 26, 2026
Member
|
Thank you for finding this oversight. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3820.
~(one's-complement) on adynamicoperand decompiled to(object)/*OpCode not supported: DynamicUnaryOperatorInstruction*/, which doesnot compile (CS1525).
VisitDynamicUnaryOperatorInstructionhandled!,-,+,++,--, andIsTrue/IsFalse, but notExpressionType.OnesComplement,so it fell through to the unsupported-opcode error expression.
This maps
OnesComplementtoUnaryOperatorType.BitNot, matching the siblingcases.
Test
UnaryOperatorsin thePretty/DynamicTestsfixture now also exercises~a(alongside the existing
-aand+a).Verified against a build with this change by decompiling a minimal assembly:
static object M(dynamic x) => ~x;now decompiles toreturn ~x;(previouslythe unsupported-opcode placeholder).