diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs index 49d191b08..545017357 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs @@ -111,4 +111,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private Entry[]? _entries; private IEqualityComparer? _comparer; } + + public class T05_NullableUnconstrainedGeneric + { + public static TValue? Default() + { + return default(TValue); + } + + public static void CallDefault() + { +#if OPT + string? format = Default(); +#else + // With optimizations it's a stack slot, so ILSpy picks a nullable type. + // Without optimizations it's a local, so the nullability is missing. + string format = Default(); +#endif + int num = Default(); +#if CS110 && NET70 + nint num2 = Default(); +#else + int num2 = Default(); +#endif + (object, string) tuple = Default<(object, string)>(); + Console.WriteLine("No inlining"); + Console.WriteLine(format, num, num2, tuple); + } + } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs index 3d7d1e115..b6b3bef3a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs @@ -64,7 +64,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public virtual IType ChangeNullability(Nullability nullability) { - Debug.Assert(nullability == Nullability.Oblivious); + // Only some types support nullability, in the default implementation + // we just ignore the nullability change. return this; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs index 1cb4c5954..3eff3c142 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IType ChangeNullability(Nullability nullability) { - if (nullability == Nullability.Oblivious) + if (nullability == Nullability.Oblivious || IsReferenceType == false) return this; else return new NullabilityAnnotatedType(this, nullability); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs index 7a41c50c0..41ae7ee70 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -18,9 +18,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation Debug.Assert(nullability != Nullability.Oblivious); // Due to IType -> concrete type casts all over the type system, we can insert // the NullabilityAnnotatedType wrapper only in some limited places. - Debug.Assert(type is ITypeDefinition + Debug.Assert(type is ITypeDefinition { IsReferenceType: not false } || type.Kind == TypeKind.Dynamic - || type.Kind == TypeKind.Unknown || (type is ITypeParameter && this is ITypeParameter)); this.nullability = nullability; } diff --git a/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs b/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs index 12601f9a4..c7cf02428 100644 --- a/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/SpecialType.cs @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public override IType ChangeNullability(Nullability nullability) { - if (nullability == base.Nullability) + if (nullability == base.Nullability || Kind is not TypeKind.Dynamic) return this; else return new NullabilityAnnotatedType(this, nullability);