Skip to content

Fix #3820: decompile dynamic ~ as ~x instead of an unsupported-opcode error - #3822

Merged
siegfriedpammer merged 4 commits into
icsharpcode:masterfrom
sailro:fix-dynamic-onescomplement
Jun 26, 2026
Merged

Fix #3820: decompile dynamic ~ as ~x instead of an unsupported-opcode error#3822
siegfriedpammer merged 4 commits into
icsharpcode:masterfrom
sailro:fix-dynamic-onescomplement

Conversation

@sailro

@sailro sailro commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #3820.

~ (one's-complement) on a dynamic operand decompiled to
(object)/*OpCode not supported: DynamicUnaryOperatorInstruction*/, which does
not compile (CS1525). VisitDynamicUnaryOperatorInstruction handled !, -,
+, ++, --, and IsTrue/IsFalse, but not ExpressionType.OnesComplement,
so it fell through to the unsupported-opcode error expression.

This maps OnesComplement to UnaryOperatorType.BitNot, matching the sibling
cases.

Test

UnaryOperators in the Pretty/DynamicTests fixture now also exercises ~a
(alongside the existing -a and +a).

Verified against a build with this change by decompiling a minimal assembly:
static object M(dynamic x) => ~x; now decompiles to return ~x; (previously
the unsupported-opcode placeholder).

…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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.OnesComplement to UnaryOperatorType.BitNot in VisitDynamicUnaryOperatorInstruction.
  • Extend the Pretty/DynamicTests UnaryOperators test case to include ~a alongside existing -a and +a coverage.

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
siegfriedpammer self-requested a review June 26, 2026 10:42
@siegfriedpammer

Copy link
Copy Markdown
Member

Thank you for finding this oversight.

@siegfriedpammer
siegfriedpammer merged commit 5d7d5dd into icsharpcode:master Jun 26, 2026
7 checks passed
@sailro
sailro deleted the fix-dynamic-onescomplement branch June 26, 2026 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decompiler emits (object)/*OpCode not supported: DynamicUnaryOperatorInstruction*/ for ~ (one's-complement) on a dynamic operand (CS1525)

4 participants