From 9238c3b63c533b30be04218fa47a40958943118b Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sun, 16 Aug 2026 10:48:28 +0200 Subject: [PATCH] Shorten default(T) in place, and say why operators keep the type Mutating the DefaultValueExpression is enough here; ConvertTo already hands out mutated input nodes elsewhere (UnwrapChild), so building a replacement node and copying the annotations over bought nothing. The operator special case is easy to mistake for a cosmetic preference, because the null literal is accepted in the same position: it converts only to reference and nullable types, so it still narrows operator overload resolution, whereas the default literal converts to everything and C# rejects it outright for every binary operator except == and !=. Assisted-by: Claude:claude-opus-5[1m]:Claude Code --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 4 +++- .../CSharp/TranslatedExpression.cs | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 126ac8608..d4cb8e090 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1079,7 +1079,9 @@ namespace ICSharpCode.Decompiler.CSharp { // Operator calls do not survive as calls: ReplaceMethodCallsWithOperators turns // them into operator or cast syntax, where the operand determines which operator - // is resolved, so it must keep its explicit type. + // is resolved, so it must keep its explicit type. Unlike the null literal, which + // still narrows the candidate set, the default literal converts to every type: + // C# rejects it as the operand of any binary operator except == and != (CS8310). arg = arg.RestoreDefaultLiteralType(expressionBuilder); } diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index d36ddaf3a..4d40d998f 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -229,16 +229,16 @@ namespace ICSharpCode.Decompiler.CSharp // Make explicit conversion implicit, if possible if (allowImplicitConversion) { - if (Expression is DefaultValueExpression { Type: not null } + if (Expression is DefaultValueExpression { Type: not null } defaultValue && expressionBuilder.settings.DefaultLiterals) { // The target type is supplied by the context, so "default(T)" can be // shortened to the C# 7.1 default literal. - var shortened = new DefaultValueExpression(); - shortened.CopyAnnotationsFrom(Expression); - shortened.RemoveAnnotations(); - return shortened.WithRR(new DefaultLiteralResolveResult(type)) - .WithoutILInstruction(); + defaultValue.Type = null; + defaultValue.RemoveAnnotations(); + var literalRR = new DefaultLiteralResolveResult(type); + defaultValue.AddAnnotation(literalRR); + return new TranslatedExpression(defaultValue, literalRR); } switch (ResolveResult) {