Emit C# 7.1 default literals where the context supplies the type

Shortening default(T) is the same problem as removing the redundant cast
around a lambda whose delegate type the context already fixes, so it uses
the same mechanism: ConvertTo makes the explicit type implicit when the
conversion is an identity conversion and the caller allows an implicit
one. The literal keeps the type it was shortened from, so any later
conversion to a different type - or any context that requires an explicit
type, such as an overload resolution recheck falling back to CastArguments
- can spell default(T) out again. That keeps the value intact where the
bare literal would change it, e.g. "object o = default(SomeStruct)", which
boxes a non-null struct while "default" would be null.

Because the shortened literal resolves to DefaultLiteralResolveResult,
CallBuilder's existing overload resolution recheck sees a real default
literal and rejects ambiguous calls on its own; no separate bookkeeping
about which arguments may stay untyped is needed. Only the contexts that
supply no target type at all restore the explicit form: an awaited
expression, and arguments of operator methods, which later become operator
or cast syntax rather than calls.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
This commit is contained in:
Christoph Wille
2026-08-22 21:35:22 +02:00
committed by Siegfried Pammer
parent f3f80cd463
commit fe9f6db3ee
55 changed files with 1104 additions and 99 deletions
@@ -482,6 +482,24 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task DefaultLiteral([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task OverloadResolution([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task TargetTypedDefault([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task PatternMatching([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions)
{
@@ -1638,14 +1638,20 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
Assert.That(c.Method.DeclaringType.Name, Is.EqualTo("OperatorInBaseClass"));
}
[Test, Ignore("C# standard 10.2.16 is not implemented: CSharpConversions.ImplicitConversion has a TODO for default literal conversions, and no ResolveResult represents a typeless default literal")]
[Test]
public void DefaultLiteralConversions()
{
// C# standard 10.2.16: an implicit conversion exists from a default_literal to
// any type, producing the default value of the inferred type. Once the semantic
// model gains a typeless default-literal ResolveResult, this test should assert
// that it converts to int, string, int? and type parameters.
Assert.Fail("Default literal conversions are not implemented.");
// C# standard 10.2.16: an implicit conversion exists from a default_literal to any type
var defaultLiteral = new DefaultLiteralResolveResult();
// a default_value_expression is a constant expression (C# standard 12.8.21)
Assert.That(defaultLiteral.IsCompileTimeConstant);
Assert.That(conversions.ImplicitConversion(defaultLiteral, compilation.FindType(KnownTypeCode.Int32)), Is.EqualTo(C.DefaultLiteralConversion));
Assert.That(conversions.ImplicitConversion(defaultLiteral, compilation.FindType(KnownTypeCode.String)), Is.EqualTo(C.DefaultLiteralConversion));
Assert.That(conversions.ImplicitConversion(defaultLiteral, compilation.FindType(typeof(int?))), Is.EqualTo(C.DefaultLiteralConversion));
ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
Assert.That(conversions.ImplicitConversion(defaultLiteral, t), Is.EqualTo(C.DefaultLiteralConversion));
// explicit conversions include all implicit conversions, so (T)default is also valid
Assert.That(conversions.ExplicitConversion(defaultLiteral, compilation.FindType(KnownTypeCode.Int32)), Is.EqualTo(C.DefaultLiteralConversion));
}
[Test, Ignore("C# standard 10.2.18 is not implemented: no ResolveResult represents a switch expression; the decompiler converts each arm separately in ILAst")]
@@ -43,8 +43,82 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Issue2444.M2();
Issue2741.B.Test(new Issue2741.C());
ExtensionMethodDemo.Issue2165.Test();
#if CS71
DefaultLiteralTests();
#endif
}
#if CS71
static void DefaultLiteralTests()
{
// The decompiled output may shorten default(T) to a default literal;
// re-compilation must still pick the same overloads and operators.
DefaultOverload(default(DataStruct));
DefaultOverload(default(OtherStruct));
DefaultNullableOverload(default(DataStruct));
Console.WriteLine(default(DataStruct) == new DataStruct());
Console.WriteLine(GenericDefault("x", default));
Console.WriteLine(GenericDefault(42, default));
}
struct DataStruct
{
public int Field;
public static bool operator ==(DataStruct a, DataStruct b)
{
Console.WriteLine("DataStruct operator ==");
return a.Field == b.Field;
}
public static bool operator !=(DataStruct a, DataStruct b)
{
return a.Field != b.Field;
}
public override bool Equals(object obj)
{
return obj is DataStruct other && Field == other.Field;
}
public override int GetHashCode()
{
return Field;
}
}
struct OtherStruct
{
public int Field;
}
static void DefaultOverload(DataStruct data)
{
Console.WriteLine("DefaultOverload(DataStruct)");
}
static void DefaultOverload(OtherStruct data)
{
Console.WriteLine("DefaultOverload(OtherStruct)");
}
static void DefaultNullableOverload(DataStruct data)
{
Console.WriteLine("DefaultNullableOverload(DataStruct)");
}
static void DefaultNullableOverload(DataStruct? data)
{
Console.WriteLine("DefaultNullableOverload(DataStruct?)");
}
static T GenericDefault<T>(T a, T b)
{
Console.WriteLine("GenericDefault: " + typeof(T).Name);
return b;
}
#endif
#region ConstructorTest
static void ConstructorTest()
{
@@ -5,8 +5,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
private void dgvItemList_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
string text = default(string);
string s = default(string);
string text = default;
string s = default;
switch (text)
{
case "rowno":
@@ -6,7 +6,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
public static implicit operator ReadOnlySpan<char>(object o)
{
return default(ReadOnlySpan<char>);
return default;
}
public static ReadOnlySpan<char> ConvertString(string s)
@@ -35,7 +35,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
{
public TaskAwaiter GetAwaiter()
{
#if CS71
return default;
#else
return default(TaskAwaiter);
#endif
}
}
}
@@ -51,7 +55,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
{
public ValueTask DisposeAsync()
{
return default(ValueTask);
return default;
}
}
#endif
@@ -247,7 +251,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
{
public static TaskAwaiter GetAwaiter(this IAwaitableMarker marker)
{
#if CS71
return default;
#else
return default(TaskAwaiter);
#endif
}
public static TaskAwaiter GetAwaiter(this int millisecondsDelay)
@@ -257,12 +265,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
public static TaskAwaiter GetAwaiter(this Action action)
{
#if CS71
return default;
#else
return default(TaskAwaiter);
#endif
}
public static TaskAwaiter<T[]> GetAwaiter<T>(this IEnumerable<Task<T>> tasks)
{
#if CS71
return default;
#else
return default(TaskAwaiter<T[]>);
#endif
}
#if CS70 && !NET40
@@ -573,7 +589,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
public TaskAwaiter Self()
{
#if CS71
return default;
#else
return default(TaskAwaiter);
#endif
}
}
#endif
@@ -582,7 +602,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
{
public TaskAwaiter GetAwaiter()
{
#if CS71
return default;
#else
return default(TaskAwaiter);
#endif
}
}
@@ -609,7 +633,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
{
TaskAwaiter IAwaitable.GetAwaiter()
{
#if CS71
return default;
#else
return default(TaskAwaiter);
#endif
}
}
@@ -618,7 +646,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
{
TaskAwaiter IAwaitable.GetAwaiter()
{
#if CS71
return default;
#else
return default(TaskAwaiter);
#endif
}
}
@@ -626,7 +658,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
{
public TaskAwaiter<T> GetAwaiter()
{
#if CS71
return default;
#else
return default(TaskAwaiter<T>);
#endif
}
}
@@ -664,7 +700,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait
public TaskAwaiter GetAwaiter()
{
Counter++;
#if CS71
return default;
#else
return default(TaskAwaiter);
#endif
}
}
}
@@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static async void TestAsyncUsingStruct()
{
await using (AsyncDisposableStruct asyncDisposableStruct = default(AsyncDisposableStruct))
await using (AsyncDisposableStruct asyncDisposableStruct = default)
{
Use(asyncDisposableStruct);
}
@@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static async void TestAsyncUsingNullableStruct()
{
await using (AsyncDisposableStruct? asyncDisposableStruct = new AsyncDisposableStruct?(default(AsyncDisposableStruct)))
await using (AsyncDisposableStruct? asyncDisposableStruct = new AsyncDisposableStruct?(default))
{
Use(asyncDisposableStruct);
}
@@ -5024,7 +5024,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
public static void CustomStructAddTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p += default(CustomStruct);
num += default(CustomStruct);
Use(ref num);
@@ -5051,7 +5055,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructSubtractTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p -= default(CustomStruct);
num -= default(CustomStruct);
Use(ref num);
@@ -5078,7 +5086,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructMultiplyTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p *= default(CustomStruct);
num *= default(CustomStruct);
Use(ref num);
@@ -5105,7 +5117,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructDivideTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p /= default(CustomStruct);
num /= default(CustomStruct);
Use(ref num);
@@ -5132,7 +5148,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructModulusTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p %= default(CustomStruct);
num %= default(CustomStruct);
Use(ref num);
@@ -5159,7 +5179,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructLeftShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p <<= 5;
num <<= 5;
Use(ref num);
@@ -5186,7 +5210,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructRightShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p >>= 5;
num >>= 5;
Use(ref num);
@@ -5239,7 +5267,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructBitAndTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p &= default(CustomStruct);
num &= default(CustomStruct);
Use(ref num);
@@ -5266,7 +5298,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructBitOrTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p |= default(CustomStruct);
num |= default(CustomStruct);
Use(ref num);
@@ -5293,7 +5329,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructBitXorTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
p ^= default(CustomStruct);
num ^= default(CustomStruct);
Use(ref num);
@@ -5320,7 +5360,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructPostIncTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
X(p++);
X(num++);
Use(ref num);
@@ -5347,7 +5391,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructPreIncTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
X(++p);
X(++num);
Use(ref num);
@@ -5373,7 +5421,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
public static void CustomStructPostDecTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
X(p--);
X(num--);
Use(ref num);
@@ -5400,7 +5452,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CustomStructPreDecTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
#if CS71
CustomStruct num = default;
#else
CustomStruct num = default(CustomStruct);
#endif
X(--p);
X(--num);
Use(ref num);
@@ -79,7 +79,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static implicit operator MyInt(int x)
{
return default(MyInt);
return default;
}
}
@@ -89,8 +89,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void Deconstruct(out T a, out T2 b)
{
a = default(T);
b = default(T2);
a = default;
b = default;
}
}
@@ -100,9 +100,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void Deconstruct(out T a, out T2 b, out T3 c)
{
a = default(T);
b = default(T2);
c = default(T3);
a = default;
b = default;
c = default;
}
}
@@ -112,8 +112,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void Deconstruct(out T a, out T2 b)
{
a = default(T);
b = default(T2);
a = default;
b = default;
}
}
@@ -177,7 +177,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private StructDeconstructionSource<T, T2> GetStructSource<T, T2>()
{
return default(StructDeconstructionSource<T, T2>);
return default;
}
private ref T GetRef<T>()
@@ -187,12 +187,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private (T, T2) GetTuple<T, T2>()
{
return default((T, T2));
return default;
}
private (T, T2, T3) GetTuple<T, T2, T3>()
{
return default((T, T2, T3));
return default;
}
private List<T> GetList<T>()
@@ -0,0 +1,139 @@
// Copyright (c) 2026 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class DefaultLiteral
{
public struct Data
{
public int Field;
public static Data operator +(Data a, Data b)
{
return new Data {
Field = a.Field + b.Field
};
}
}
public struct OtherData
{
public int Field;
}
public int this[Data d] => d.Field;
public int DeclarationWithInitializer()
{
Data data = default;
return data.Field + data.Field;
}
public int Assignment(Data data)
{
int field = data.Field;
data = default;
return field + data.Field;
}
public Data Return()
{
return default;
}
public T ReturnGeneric<T>()
{
return default;
}
public async Task<Data> ReturnAsync()
{
await Task.Yield();
return default;
}
public string AmbiguousArgumentStaysTyped()
{
return Overloaded(default(Data));
}
public string BetterConversionTargetArgument()
{
return OverloadedNullable(default);
}
public int UnambiguousArgument()
{
return Single(default);
}
public string BoxedArgumentStaysTyped()
{
return Boxed(default(Data));
}
public int IndexerArgument()
{
return this[default];
}
public Data OperatorOperandStaysTyped(Data data)
{
return data + default(Data);
}
public string NonIdentityConversionsStayTyped()
{
object obj = default(Data);
return obj.ToString() + obj.ToString();
}
private int Single(Data data)
{
return data.Field;
}
private string Boxed(object o)
{
return o.ToString();
}
private string Overloaded(Data x)
{
return "Data";
}
private string Overloaded(OtherData x)
{
return "OtherData";
}
private string OverloadedNullable(Data x)
{
return "Data";
}
private string OverloadedNullable(Data? x)
{
return "Data?";
}
}
}
@@ -261,7 +261,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
{
public Func<TCaptured> GetFunc(Func<TNonCaptured, TCaptured> f)
{
#if CS71
TCaptured captured = f(default);
#else
TCaptured captured = f(default(TNonCaptured));
#endif
return () => {
Console.WriteLine(captured.GetType().FullName);
return captured;
@@ -966,7 +966,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static DateTime ParseDateTime(this object str)
{
#if CS71
return default;
#else
return default(DateTime);
#endif
}
}
}
@@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public static implicit operator ReadOnlySpan<int>(SpanConvertible c)
{
return default(ReadOnlySpan<int>);
return default;
}
}
@@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void OutSpanOrByValue(out ReadOnlySpan<int> s)
{
s = default(ReadOnlySpan<int>);
s = default;
}
public static void OutSpanOrByValue(ReadOnlySpan<int> s)
@@ -73,7 +73,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
public S this[int index] {
get {
#if CS71
return default;
#else
return default(S);
#endif
}
set {
}
@@ -81,7 +85,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
public S this[object key] {
get {
#if CS71
return default;
#else
return default(S);
#endif
}
set {
}
@@ -158,7 +166,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
public StructData(int initialValue)
{
#if CS71
this = default;
#else
this = default(StructData);
#endif
Field = initialValue;
Property = initialValue;
}
@@ -125,12 +125,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public Byte16 GetByte16()
{
return default(Byte16);
return default;
}
public Generic16<T> GetGeneric<T>()
{
return default(Generic16<T>);
return default;
}
public int GetIndex()
@@ -8,13 +8,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
[StructLayout(LayoutKind.Sequential, Size = 1)]
public readonly struct fsResult
{
public static fsResult Success => default(fsResult);
public static fsResult Failure => default(fsResult);
public static fsResult Success => default;
public static fsResult Failure => default;
public bool Succeeded => true;
public bool Failed => false;
public static fsResult operator +(fsResult a, fsResult b)
{
return default(fsResult);
return default;
}
}
@@ -6,13 +6,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3571_B
[StructLayout(LayoutKind.Sequential, Size = 1)]
public readonly struct fsResult
{
public static fsResult Success => default(fsResult);
public static fsResult Failure => default(fsResult);
public static fsResult Success => default;
public static fsResult Failure => default;
public bool Succeeded => true;
public bool Failed => false;
public static fsResult operator +(fsResult a, fsResult b)
{
return default(fsResult);
return default;
}
}
@@ -25,13 +25,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3571_Helper
[StructLayout(LayoutKind.Sequential, Size = 1)]
public readonly struct fsResult
{
public static fsResult Success => default(fsResult);
public static fsResult Failure => default(fsResult);
public static fsResult Success => default;
public static fsResult Failure => default;
public bool Succeeded => true;
public bool Failed => false;
public static fsResult operator +(fsResult a, fsResult b)
{
return default(fsResult);
return default;
}
}
}
@@ -14,7 +14,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
get {
if (i >= Length || i < 0)
{
#if CS71
return default;
#else
return default(T);
#endif
}
if (results[i] != null && results[i].Equals(default(T)))
{
@@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public virtual U? ReturnOnly<U>()
{
return default(U);
return default;
}
public virtual T?[] Nested<T>(T?[] values)
@@ -78,7 +78,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public override U? ReturnOnly<U>() where U : default
{
return default(U);
return default;
}
public override T?[] Nested<T>(T?[] values) where T : default
@@ -38,7 +38,7 @@ namespace LocalFunctions
public int MixedLocalFunction<T2>() where T2 : ICloneable, IConvertible
{
#pragma warning disable CS0219
T2 t2 = default(T2);
T2 t2 = default;
object z = this;
for (int i = 0; i < 10; i++)
{
@@ -52,7 +52,7 @@ namespace LocalFunctions
int NonStaticMethod<T3>(int unused)
#endif
{
t2 = default(T2);
t2 = default;
int l = 0;
return NonStaticMethod3<T1>() + NonStaticMethod3<T2>() + z.GetHashCode();
int NonStaticMethod3<T4>()
@@ -118,7 +118,7 @@ namespace LocalFunctions
public int MixedLocalFunction2Delegate<T2>() where T2 : ICloneable, IConvertible
{
T2 t2 = default(T2);
T2 t2 = default;
object z = this;
for (int i = 0; i < 10; i++)
{
@@ -126,7 +126,7 @@ namespace LocalFunctions
i2 += StaticInvokeAsFunc(NonStaticMethod<object>);
int NonStaticMethod<T3>()
{
t2 = default(T2);
t2 = default;
int l = 0;
return StaticInvokeAsFunc(NonStaticMethod3<T1>) + StaticInvokeAsFunc(NonStaticMethod3<T2>) + z.GetHashCode();
int NonStaticMethod3<T4>()
@@ -155,7 +155,7 @@ namespace LocalFunctions
int StaticInvokeAsFunc2<T>(Func<T, int> func)
#endif
{
return func(default(T));
return func(default);
}
#if CS80
static int StaticMethod<T3>() where T3 : struct
@@ -209,19 +209,19 @@ namespace LocalFunctions
public static void Test_CaptureT<T2>()
{
#pragma warning disable CS0219
T2 t2 = default(T2);
T2 t2 = default;
Method<int>();
void Method<T3>()
{
t2 = default(T2);
t2 = default;
T2 t2x = t2;
T3 t3 = default(T3);
T3 t3 = default;
Method2();
void Method2()
{
t2 = default(T2);
t2 = default;
t2x = t2;
t3 = default(T3);
t3 = default;
}
}
#pragma warning restore CS0219
@@ -515,7 +515,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static T LastOrDefault<T>(IEnumerable<T> items)
{
#if CS71
T result = default;
#else
T result = default(T);
#endif
foreach (T item in items)
{
result = item;
@@ -37,7 +37,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void TestB(S x, ref S y)
{
b[5, 3] = new S[10];
#if CS71
b[5, 3][0] = default;
#else
b[5, 3][0] = default(S);
#endif
b[5, 3][1] = x;
b[5, 3][2] = y;
}
@@ -50,7 +50,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public readonly int ReadonlyIntVal;
public MyClass Field;
public MyStruct? Property1 => null;
#if CS71
public MyStruct Property2 => default;
#else
public MyStruct Property2 => default(MyStruct);
#endif
public MyStruct? this[int index] => null;
public MyStruct? Method1(int arg)
{
@@ -58,7 +62,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
public MyStruct Method2(int arg)
{
#if CS71
return default;
#else
return default(MyStruct);
#endif
}
public void Done()
@@ -118,7 +118,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public static TValue? Default<TValue>()
{
return default(TValue);
return default;
}
public static void CallDefault()
@@ -204,7 +204,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
[return: MaybeNull]
public T FirstOrDefault<T>(IEnumerable<T> source)
{
return default(T);
return default;
}
}
}
@@ -46,7 +46,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private bool TryGet<T>(out T result)
{
result = default(T);
result = default;
return true;
}
@@ -0,0 +1,160 @@
// Copyright (c) 2026 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#pragma warning disable 660, 661
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class OverloadResolution
{
public struct Data
{
public int Field;
public static bool operator ==(Data a, Data b)
{
return a.Field == b.Field;
}
public static bool operator !=(Data a, Data b)
{
return a.Field != b.Field;
}
}
public struct OtherData
{
public int Field;
}
public void IntegerOverloads()
{
Integer(1);
Integer((short)1);
Integer(1L);
}
public void ReferenceTypeOverloads()
{
RefType("string");
RefType((object)"string");
RefType(null);
RefType((object)null);
}
public void NullableOverloads()
{
NullableInt(1);
NullableInt((int?)1);
NullableInt(null);
}
public void ParamsOverloads(int n)
{
Params(1);
Params(1, 2);
Params(default(int), default(int), default(int));
Params(new int[n]);
}
public void GenericOverloads()
{
Generic(1);
Generic<int>(1);
}
public string AmbiguousDefaultArgumentStaysTyped()
{
return Ambiguous(default(Data));
}
public string DefaultArgumentWithBetterConversionTarget()
{
return WithNullable(default);
}
public bool EqualityWithDefaultStaysTyped(Data data)
{
return data == default(Data);
}
private static void Integer(short s)
{
}
private static void Integer(int i)
{
}
private static void Integer(long l)
{
}
private static void RefType(object o)
{
}
private static void RefType(string s)
{
}
private static void NullableInt(int i)
{
}
private static void NullableInt(int? i)
{
}
private static void Params(int i)
{
}
private static void Params(params int[] xs)
{
}
private static void Generic(int i)
{
}
private static void Generic<T>(T a)
{
}
private static string Ambiguous(Data x)
{
return "Data";
}
private static string Ambiguous(OtherData x)
{
return "OtherData";
}
private static string WithNullable(Data x)
{
return "Data";
}
private static string WithNullable(Data? x)
{
return "Data?";
}
}
}
@@ -56,7 +56,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public unsafe static void AssignmentGuidPointerToDateTimePointerDefault(Guid* ptr)
{
#if CS71
((DateTime*)ptr)[2] = default;
#else
((DateTime*)ptr)[2] = default(DateTime);
#endif
}
public unsafe static void AssignmentGuidPointerToDateTimePointer_2(Guid* ptr)
@@ -66,7 +70,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public unsafe static void AssignmentGuidPointerToDateTimePointerDefault_2(Guid* ptr)
{
#if CS71
*(DateTime*)(ptr + 2) = default;
#else
*(DateTime*)(ptr + 2) = default(DateTime);
#endif
}
public unsafe static DateTime AccessGuidPointerToDateTimePointer(Guid* ptr)
@@ -33,12 +33,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public static Maybe<TResult> Select<T, TResult>(this Maybe<T> a, Func<T, TResult> fn)
{
#if CS71
return default;
#else
return default(Maybe<TResult>);
#endif
}
public static Maybe<T> Where<T>(this Maybe<T> a, Func<T, bool> predicate)
{
#if CS71
return default;
#else
return default(Maybe<T>);
#endif
}
}
@@ -48,13 +56,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public Maybe<int> Value;
#if CS60
#if CS71
public Maybe<int> this[int index] => default;
#elif CS60
public Maybe<int> this[int index] => default(Maybe<int>);
#endif
public Func<Maybe<int>> Factory()
{
#if CS71
return () => default;
#else
return () => default(Maybe<int>);
#endif
}
}
@@ -66,12 +66,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public Span<int> ScopedSpan(scoped Span<int> span)
{
return default(Span<int>);
return default;
}
public void OutSpan(out Span<int> span)
{
span = default(Span<int>);
span = default;
}
public void CaptureIntoRef(ref Holder holder, Span<int> inner)
@@ -81,13 +81,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void CaptureIntoOut(out Holder holder, Span<int> inner)
{
holder = default(Holder);
holder = default;
holder.Inner = inner;
}
public void CaptureRefIntoOut(out Holder holder, ref int value)
{
holder = default(Holder);
holder = default;
holder.Inner = new Span<int>(ref value);
}
@@ -105,7 +105,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public Span<int> NoCaptureOut(out int value)
{
value = 0;
return default(Span<int>);
return default;
}
public Span<int> Identity(Span<int> span)
@@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ReassignScopedSpanFromOut(bool b)
{
int value = 0;
scoped Span<int> span = default(Span<int>);
scoped Span<int> span = default;
if (b)
{
span = CaptureOut(out value);
@@ -147,7 +147,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ImplicitScopedOutDoesNotCapture()
{
Span<int> span = default(Span<int>);
Span<int> span = default;
span = NoCaptureOut(out var value);
Console.WriteLine(span.Length);
return span.Length + value;
@@ -155,8 +155,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ReassignScopedSpanFromReceiver(bool b)
{
UnscopedRefStruct unscopedRefStruct = default(UnscopedRefStruct);
scoped Span<int> span = default(Span<int>);
UnscopedRefStruct unscopedRefStruct = default;
scoped Span<int> span = default;
if (b)
{
span = unscopedRefStruct.AsSpan();
@@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ReassignScopedSpanFromRefParameter(bool b, ref int value)
{
scoped Span<int> span = default(Span<int>);
scoped Span<int> span = default;
if (b)
{
span = CreateAndCapture(ref value);
@@ -176,7 +176,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ReassignScopedSpanFromStackAlloc(bool b)
{
scoped Span<int> span = default(Span<int>);
scoped Span<int> span = default;
if (b)
{
span = stackalloc int[1];
@@ -186,7 +186,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ReassignScopedSpanFromScopedValue(bool b, scoped Span<int> value)
{
scoped Span<int> span = default(Span<int>);
scoped Span<int> span = default;
if (b)
{
span = value;
@@ -197,7 +197,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ReassignScopedSpanFromNestedCall(bool b)
{
int value = 0;
scoped Span<int> span = default(Span<int>);
scoped Span<int> span = default;
if (b)
{
span = Identity(CreateAndCapture(ref value));
@@ -208,7 +208,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ReassignScopedSpanFromLocalCopy(bool b)
{
Span<int> span = stackalloc int[1];
scoped Span<int> span2 = default(Span<int>);
scoped Span<int> span2 = default;
if (b)
{
span2 = span;
@@ -219,7 +219,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ReassignScopedSpanFromScopedRefValue(bool b)
{
Span<int> span = stackalloc int[1];
scoped Span<int> span2 = default(Span<int>);
scoped Span<int> span2 = default;
if (b)
{
span2 = ScopedRefSpan(ref span);
@@ -252,7 +252,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ClassParameterReceiverDoesNotNarrow(SpanProvider provider, bool b)
{
Span<int> span = default(Span<int>);
Span<int> span = default;
if (b)
{
span = provider.GetBuffer();
@@ -263,7 +263,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ClassLocalReceiverDoesNotNarrow(bool b)
{
SpanProvider spanProvider = new SpanProvider();
Span<int> span = default(Span<int>);
Span<int> span = default;
if (b)
{
span = spanProvider.GetBuffer();
@@ -283,7 +283,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int FieldStoreRequiresScoped()
{
scoped Holder holder = default(Holder);
scoped Holder holder = default;
Span<int> inner = stackalloc int[4];
holder.Inner = inner;
return holder.Inner.Length;
@@ -291,7 +291,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ReceiverCallRequiresScoped()
{
scoped Holder holder = default(Holder);
scoped Holder holder = default;
Span<int> inner = stackalloc int[4];
holder.Set(inner);
return holder.Inner.Length;
@@ -299,7 +299,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int ExtensionReceiverCallRequiresScoped()
{
scoped Holder holder = default(Holder);
scoped Holder holder = default;
Span<int> inner = stackalloc int[4];
holder.SetExtension(inner);
return holder.Inner.Length;
@@ -307,7 +307,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int RefArgumentCallRequiresScoped()
{
scoped Holder holder = default(Holder);
scoped Holder holder = default;
Span<int> inner = stackalloc int[4];
CaptureIntoRef(ref holder, inner);
return holder.Inner.Length;
@@ -315,7 +315,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int OutArgumentCallRequiresScoped()
{
scoped Holder holder = default(Holder);
scoped Holder holder = default;
Span<int> inner = stackalloc int[4];
CaptureIntoOut(out holder, inner);
return holder.Inner.Length;
@@ -324,28 +324,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int OutArgumentCapturesRefRequiresScoped()
{
int value = 0;
scoped Holder holder = default(Holder);
scoped Holder holder = default;
CaptureRefIntoOut(out holder, ref value);
return holder.Inner[0];
}
public Holder RefArgumentWideValueDoesNotRequireScoped(Span<int> wide)
{
Holder holder = default(Holder);
Holder holder = default;
CaptureIntoRef(ref holder, wide);
return holder;
}
public Holder OutArgumentWideValueDoesNotRequireScoped(Span<int> wide)
{
Holder holder = default(Holder);
Holder holder = default;
CaptureIntoOut(out holder, wide);
return holder;
}
public int RefReturnFieldStoreRequiresScoped()
{
scoped Holder holder = default(Holder);
scoped Holder holder = default;
Span<int> inner = stackalloc int[4];
Identity(ref holder).Inner = inner;
return holder.Inner.Length;
@@ -353,14 +353,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public Holder RefReturnFieldStoreWideValueDoesNotRequireScoped(Span<int> wide)
{
Holder holder = default(Holder);
Holder holder = default;
Identity(ref holder).Inner = wide;
return holder;
}
public Holder ReadonlyReceiverDoesNotRequireScoped(bool b)
{
Holder result = default(Holder);
Holder result = default;
if (b)
{
Span<int> inner = stackalloc int[4];
@@ -371,7 +371,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public Holder ReadonlyExtensionReceiverDoesNotRequireScoped(bool b)
{
Holder holder = default(Holder);
Holder holder = default;
if (b)
{
Span<int> inner = stackalloc int[4];
@@ -466,7 +466,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public Span<int> GetBuffer()
{
return default(Span<int>);
return default;
}
}
@@ -100,7 +100,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static SpanFactory Create(int size)
{
return default(SpanFactory);
return default;
}
}
@@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static T CreateDefault<T>() where T : allows ref struct
{
return default(T);
return default;
}
public static void CombinedUnmanaged<T>() where T : unmanaged, allows ref struct
@@ -204,7 +204,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void InvokeDelegate(RefStructAction<Span<int>> action)
{
action(default(Span<int>));
action(default);
}
public static void LocalFunctionAllows()
@@ -217,7 +217,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void CapturingLocalFunction<T>(int seed) where T : allows ref struct
{
Nested(default(T));
Nested(default);
Console.WriteLine(seed);
void Nested(scoped T value)
{
@@ -98,7 +98,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.StaticAbstractInterfaceM
}
static virtual implicit operator T(string s)
{
return default(T);
return default;
}
static virtual explicit operator string(T t)
{
@@ -150,7 +150,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static TReturn Get<TReturn>()
{
#if CS71
return default;
#else
return default(TReturn);
#endif
}
}
}
@@ -30,7 +30,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#if CS100
public StructWithDefaultCtor M()
{
#if CS71
return default;
#else
return default(StructWithDefaultCtor);
#endif
}
public StructWithDefaultCtor M2()
@@ -0,0 +1,227 @@
// Copyright (c) 2026 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq.Expressions;
using System.Threading;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class TargetTypedDefault
{
public struct ValueHolder
{
public int Value;
}
public enum Flavor
{
None,
Sweet
}
#if !OPT
public Guid guidField = default;
#else
public Guid guidField;
#endif
public void OptGuid(Guid guid = default(Guid))
{
}
public void OptCancellationToken(CancellationToken cancellationToken = default(CancellationToken))
{
}
public void OptTimeSpan(TimeSpan timeSpan = default(TimeSpan))
{
}
public void OptDateTime(DateTime dateTime = default(DateTime))
{
}
public void OptCustomStruct(ValueHolder holder = default(ValueHolder))
{
}
public void OptDecimal(decimal d = 0m)
{
}
public void OptNullable(int? x = null)
{
}
public void OptEnum(Flavor flavor = Flavor.None)
{
}
public void OptString(string s = null)
{
}
public void OptInt(int i = 0)
{
}
public void OptGeneric<T>(T value = default(T))
{
}
public void OptGenericStruct<T>(T value = default(T)) where T : struct
{
}
public void OptTuple((int, string) pair = default((int, string)))
{
}
public void CallsWithExplicitDefaults()
{
OptGuid();
OptCancellationToken();
OptTimeSpan();
OptCustomStruct();
OptNullable();
OptString();
OptGeneric<Guid>();
OptTuple();
}
public void TakeGuid(Guid guid)
{
}
public void TakeInGuid(in Guid guid)
{
}
public void Over(int x)
{
}
public void Over(string s)
{
}
public void OverStruct(Guid guid)
{
}
public void OverStruct(TimeSpan timeSpan)
{
}
public void CallOverloads()
{
Over(0);
Over(null);
OverStruct(default(Guid));
OverStruct(default(TimeSpan));
}
public void ArgumentPositions(bool b, Guid guid)
{
TakeGuid(default);
TakeInGuid(default(Guid));
TakeGuid(b ? guid : default(Guid));
}
public T GenericReturn<T>()
{
return default;
}
public T GenericClassReturn<T>() where T : class
{
return null;
}
public T GenericNewReturn<T>() where T : new()
{
return default;
}
public ValueHolder StructReturn()
{
return default;
}
public (int, string) TupleReturn()
{
return default;
}
public int? NullableReturn()
{
return null;
}
public void OutDefault(out Guid guid)
{
guid = default;
}
public bool GuidIsDefault(Guid guid)
{
return guid == default(Guid);
}
public bool TimeSpanIsDefault(TimeSpan timeSpan)
{
return timeSpan == default(TimeSpan);
}
public bool IntIsDefault(int i)
{
return i == 0;
}
public bool NullableIsDefault(int? x)
{
return !x.HasValue;
}
public string CoalesceDefault(string s)
{
return s ?? null;
}
public T[] DefaultInitializedArray<T>()
{
return new T[3];
}
public unsafe int* PointerDefault()
{
return null;
}
public Expression<Func<Guid>> GuidExpressionTree()
{
return () => default;
}
public Expression<Func<int>> ZeroExpressionTree()
{
return () => 0;
}
}
}
@@ -547,7 +547,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private unsafe void Issue990()
{
#if CS71
Data data = default;
#else
Data data = default(Data);
#endif
Data* ptr = &data;
ConvertIntToFloat(ptr->Position.GetHashCode());
}
@@ -562,7 +566,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private static T Get<T>()
{
#if CS71
return default;
#else
return default(T);
#endif
}
private unsafe static ResultStruct NestedFixedBlocks(byte[] array)
@@ -36,7 +36,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static implicit operator C(bool b)
{
#if CS71
return default;
#else
return default(C);
#endif
}
}
@@ -77,12 +81,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static implicit operator C(bool b)
{
#if CS71
return default;
#else
return default(C);
#endif
}
public static implicit operator C(A a)
{
#if CS71
return default;
#else
return default(C);
#endif
}
public static bool operator ==(C a, C b)
@@ -141,12 +153,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static implicit operator T(in int val)
{
#if CS71
return default;
#else
return default(T);
#endif
}
public static explicit operator T(in long val)
{
#if CS71
return default;
#else
return default(T);
#endif
}
}
@@ -156,7 +176,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static implicit operator T(in U u)
{
#if CS71
return default;
#else
return default(T);
#endif
}
public static explicit operator int(in U u)
@@ -50,12 +50,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#if !ROSLYN3
public static implicit operator TypeB_Issue3385(TypeA_Issue3385 a)
{
#if CS71
return default;
#else
return default(TypeB_Issue3385);
#endif
}
#else
public static implicit operator TypeB_Issue3385(in TypeA_Issue3385 a)
{
#if CS71
return default;
#else
return default(TypeB_Issue3385);
#endif
}
#endif
}
@@ -191,9 +199,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void Issue3385()
{
#if ROSLYN3
#if CS71
using (TypeA_Issue3385 a = default)
#else
using (TypeA_Issue3385 a = default(TypeA_Issue3385))
#endif
#else
#if CS71
using (TypeA_Issue3385 typeA_Issue = default)
#else
using (TypeA_Issue3385 typeA_Issue = default(TypeA_Issue3385))
#endif
#endif
{
#if ROSLYN3
@@ -113,7 +113,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void UsingVarPatternBasedDispose()
{
Console.WriteLine("before using");
using RefStructWithDispose refStructWithDispose = default(RefStructWithDispose);
using RefStructWithDispose refStructWithDispose = default;
Console.WriteLine("inside using");
refStructWithDispose.Use();
}
@@ -134,7 +134,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#if !(ROSLYN && OPT) || COPY_PROPAGATION_FIXED
public static S InitObj1()
{
#if CS71
S result = default;
#else
S result = default(S);
#endif
MakeArray();
return result;
}
@@ -142,12 +146,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static S InitObj2()
{
#if CS71
return default;
#else
return default(S);
#endif
}
public static void InitObj3(out S p)
{
#if CS71
p = default;
#else
p = default(S);
#endif
}
public static S CallValueTypeCtor()
@@ -252,7 +264,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static T Get<T>()
{
#if CS71
return default;
#else
return default(T);
#endif
}
public static void CallOnTemporary()
@@ -40,7 +40,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.VBPretty
public async void AwaitDefaultYieldAwaitable()
{
#if LEGACY_VBC || (OPTIMIZE && !ROSLYN4)
YieldAwaitable yieldAwaitable2 = default(YieldAwaitable);
YieldAwaitable yieldAwaitable2 = default;
YieldAwaitable yieldAwaitable = yieldAwaitable2;
await yieldAwaitable;
#else
@@ -51,7 +51,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.VBPretty
public async void AwaitDefaultHopToThreadPool()
{
#if LEGACY_VBC || (OPTIMIZE && !ROSLYN4)
HopToThreadPoolAwaitable hopToThreadPoolAwaitable2 = default(HopToThreadPoolAwaitable);
HopToThreadPoolAwaitable hopToThreadPoolAwaitable2 = default;
HopToThreadPoolAwaitable hopToThreadPoolAwaitable = hopToThreadPoolAwaitable2;
await hopToThreadPoolAwaitable;
#else
@@ -4,6 +4,6 @@ public class Issue1906
{
public void M()
{
Console.WriteLine(Math.Min(Math.Max(long.MinValue, default(long)), long.MaxValue));
Console.WriteLine(Math.Min(Math.Max(long.MinValue, default), long.MaxValue));
}
}
@@ -1075,6 +1075,13 @@ namespace ICSharpCode.Decompiler.CSharp
}
arg = arg.ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: arg.Type.Kind != TypeKind.Dynamic);
if (method.IsOperator)
{
// 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.
arg = arg.RestoreDefaultLiteralType(expressionBuilder);
}
if (parameter.ReferenceKind != ReferenceKind.None)
{
@@ -890,11 +890,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
StartNode(defaultValueExpression);
WriteKeyword(DefaultValueExpression.DefaultKeyword);
LPar();
Space(policy.SpacesWithinTypeOfParentheses);
defaultValueExpression.Type.AcceptVisitor(this);
Space(policy.SpacesWithinTypeOfParentheses);
RPar();
if (defaultValueExpression.Type is not null)
{
LPar();
Space(policy.SpacesWithinTypeOfParentheses);
defaultValueExpression.Type.AcceptVisitor(this);
Space(policy.SpacesWithinTypeOfParentheses);
RPar();
}
EndNode(defaultValueExpression);
}
@@ -91,7 +91,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (c != Conversion.None)
return c;
// C# 9.0 spec: §10.2.16 default literal conversions
// TODO
if (resolveResult is DefaultLiteralResolveResult)
return Conversion.DefaultLiteralConversion;
if (resolveResult.IsCompileTimeConstant)
{
c = StandardImplicitConversion(resolveResult.Type, toType, allowTuple);
@@ -596,6 +596,13 @@ namespace ICSharpCode.Decompiler.CSharp
if (var.LoadCount > 0 || var.AddressCount > 0)
{
var type = settings.AnonymousTypes && var.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(var.Type);
if (resource is DefaultValueExpression)
{
// Unlike "using (expr)", the declaration spells out the type, so the
// resource may use the default literal.
resource = new TranslatedExpression(resource)
.ConvertTo(var.Type, exprBuilder, allowImplicitConversion: true);
}
var vds = new VariableDeclarationStatement(type, var.Name!, resource);
vds.Variables.Single().AddAnnotation(new ILVariableResolveResult(var, var.Type));
usingInit = vds;
@@ -37,6 +37,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public const string DefaultKeyword = "default";
[Slot("Type")]
public partial AstType Type { get; set; }
public partial AstType? Type { get; set; }
}
}
@@ -708,7 +708,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
AstType type = context.TypeSystemAstBuilder.ConvertType(v.Type);
if (v.DefaultInitialization == VariableInitKind.NeedsDefaultValue)
{
initializer = new DefaultValueExpression(type.Clone());
// The declaration always spells out the type, so the default literal is
// equivalent to default(T).
initializer = context.Settings.DefaultLiterals
? new DefaultValueExpression()
: new DefaultValueExpression(type.Clone());
}
var vds = new VariableDeclarationStatement(type, v.Name, initializer);
if (context.Settings.ScopedRef && v.ILVariable.IsScopedWithoutInitializer)
@@ -168,6 +168,20 @@ namespace ICSharpCode.Decompiler.CSharp
throw new ArgumentException("descendant must be a descendant of the current node");
}
/// <summary>
/// Undoes the shortening to the C# 7.1 default literal that <see cref="ConvertTo"/>
/// applies, restoring the original "default(T)". Use in contexts that supply no target
/// type for the literal, e.g. an awaited expression or an argument of a call that later
/// becomes a cast or an operator.
/// </summary>
public TranslatedExpression RestoreDefaultLiteralType(ExpressionBuilder expressionBuilder)
{
if (ResolveResult is not DefaultLiteralResolveResult literal)
return this;
return expressionBuilder.GetDefaultValueExpression(literal.ShortenedFrom)
.WithILInstruction(this.ILInstructions);
}
/// <summary>
/// Adds casts (if necessary) to convert this expression to the specified target type.
/// </summary>
@@ -195,11 +209,37 @@ namespace ICSharpCode.Decompiler.CSharp
public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expressionBuilder, bool checkForOverflow = false, bool allowImplicitConversion = false)
{
var type = this.Type;
if (ResolveResult is DefaultLiteralResolveResult literal)
{
if (allowImplicitConversion
&& NormalizeTypeVisitor.IgnoreNullabilityAndTuples.EquivalentTypes(literal.ShortenedFrom, targetType))
{
// The context still supplies the type the literal was shortened from.
return this;
}
// Either an explicit type is required here (e.g. overload resolution needs the
// typed form to stay unambiguous), or the context supplies a different type, in
// which case the literal would produce a different value (e.g. null instead of
// a boxed struct).
return RestoreDefaultLiteralType(expressionBuilder)
.ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion);
}
if (NormalizeTypeVisitor.IgnoreNullabilityAndTuples.EquivalentTypes(type, targetType))
{
// Make explicit conversion implicit, if possible
if (allowImplicitConversion)
{
if (Expression is DefaultValueExpression { Type: not null }
&& 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<ResolveResult>();
return shortened.WithRR(new DefaultLiteralResolveResult(type))
.WithoutILInstruction();
}
switch (ResolveResult)
{
case ConversionResolveResult conversion:
@@ -633,6 +633,14 @@ namespace ICSharpCode.Decompiler
[DecompilerSetting(CSharp.LanguageVersion.CSharp7)]
public partial bool ThrowExpressions { get; set; }
/// <summary>
/// Gets/Sets whether the C# 7.1 default literal <c>default</c> should be used instead of
/// <c>default(T)</c>, wherever the surrounding syntax already supplies the type.
/// </summary>
[Description("DecompilerSettings.UseDefaultLiterals")]
[DecompilerSetting(CSharp.LanguageVersion.CSharp7_1)]
public partial bool DefaultLiterals { get; set; }
/// <summary>
/// Gets/Sets whether implicit conversions between tuples
/// should be used in the decompiled output.
@@ -104,6 +104,11 @@ namespace ICSharpCode.Decompiler.Semantics
/// </summary>
public static readonly Conversion ExplicitSpanConversion = new BuiltinConversion(false, 14);
/// <summary>
/// C# 7.1 default literal being converted to an arbitrary type.
/// </summary>
public static readonly Conversion DefaultLiteralConversion = new BuiltinConversion(true, 15);
public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false)
{
if (operatorMethod == null)
@@ -265,6 +270,7 @@ namespace ICSharpCode.Decompiler.Semantics
public override bool IsInlineArrayConversion => type == 12;
public override bool IsImplicitSpanConversion => type == 13;
public override bool IsExplicitSpanConversion => type == 14;
public override bool IsDefaultLiteralConversion => type == 15;
public override string ToString()
{
@@ -306,6 +312,8 @@ namespace ICSharpCode.Decompiler.Semantics
return "implicit span conversion";
case 14:
return "explicit span conversion";
case 15:
return "default-literal conversion";
}
return (isImplicit ? "implicit " : "explicit ") + name + " conversion";
}
@@ -499,6 +507,10 @@ namespace ICSharpCode.Decompiler.Semantics
get { return false; }
}
public virtual bool IsDefaultLiteralConversion {
get { return false; }
}
public virtual bool IsIdentityConversion {
get { return false; }
}
@@ -0,0 +1,57 @@
// Copyright (c) 2026 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Semantics
{
/// <summary>
/// Represents a typeless default literal `default`, which is implicitly convertible
/// to any type (C# standard 10.2.16 default literal conversions).
/// </summary>
class DefaultLiteralResolveResult : ResolveResult
{
/// <summary>
/// The type of the "default(T)" expression the literal was shortened from; it is restored
/// wherever the surrounding syntax stops supplying that very type.
/// <see cref="SpecialType.UnknownType"/> if the literal does not stand for a shortened
/// expression.
/// </summary>
public readonly IType ShortenedFrom;
public DefaultLiteralResolveResult() : this(SpecialType.UnknownType)
{
}
public DefaultLiteralResolveResult(IType shortenedFrom) : base(SpecialType.NoType)
{
this.ShortenedFrom = shortenedFrom;
}
// A default_value_expression is a constant expression (C# standard 12.8.21);
// like the null literal, the typeless default literal is modeled as a constant
// with value null (the target-typed value is only known after conversion).
public override bool IsCompileTimeConstant {
get { return true; }
}
public override object ConstantValue {
get { return null; }
}
}
}
+9
View File
@@ -1640,6 +1640,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Use &quot;default&quot; literals without an explicit type.
/// </summary>
public static string DecompilerSettings_UseDefaultLiterals {
get {
return ResourceManager.GetString("DecompilerSettings.UseDefaultLiterals", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use discards.
/// </summary>
+3
View File
@@ -579,6 +579,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.UnsignedRightShift" xml:space="preserve">
<value>Unsigned right shift (&gt;&gt;&gt;)</value>
</data>
<data name="DecompilerSettings.UseDefaultLiterals" xml:space="preserve">
<value>Use "default" literals without an explicit type</value>
</data>
<data name="DecompilerSettings.UseDiscards" xml:space="preserve">
<value>Use discards</value>
</data>