Browse Source

Avoid exceptions on IType->ArrayType or IType->ITypeParameter casts due to NullabilityAnnotatedType decorator.

pull/1425/head
Daniel Grunwald 6 years ago
parent
commit
06cf9c1747
  1. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  2. 16
      ICSharpCode.Decompiler/TypeSystem/ArrayType.cs
  3. 3
      ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs
  4. 6
      ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs
  5. 56
      ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs
  6. 10
      ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -252,7 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -252,7 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ConvertTypeHelper(pt.GenericType, pt.TypeArguments);
}
if (type is NullabilityAnnotatedType nat) {
var astType = ConvertType(nat.ElementType);
var astType = ConvertType(nat.TypeWithoutAnnotation);
if (nat.Nullability == Nullability.Nullable)
astType = astType.MakeNullableType();
return astType;

16
ICSharpCode.Decompiler/TypeSystem/ArrayType.cs

@ -30,8 +30,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -30,8 +30,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
readonly int dimensions;
readonly ICompilation compilation;
public ArrayType(ICompilation compilation, IType elementType, int dimensions = 1) : base(elementType)
readonly Nullability nullability;
public ArrayType(ICompilation compilation, IType elementType, int dimensions = 1, Nullability nullability = Nullability.Oblivious) : base(elementType)
{
if (compilation == null)
throw new ArgumentNullException("compilation");
@ -39,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -39,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
throw new ArgumentOutOfRangeException("dimensions", dimensions, "dimensions must be positive");
this.compilation = compilation;
this.dimensions = dimensions;
this.nullability = nullability;
ICompilationProvider p = elementType as ICompilationProvider;
if (p != null && p.Compilation != compilation)
@ -57,12 +59,14 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -57,12 +59,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
get { return dimensions; }
}
public override Nullability Nullability => nullability;
public override IType ChangeNullability(Nullability nullability)
{
if (nullability == Nullability.Oblivious)
if (nullability == this.nullability)
return this;
else
return new NullabilityAnnotatedType(this, nullability);
return new ArrayType(compilation, elementType, dimensions, nullability);
}
public override string NameSuffix {
@ -83,7 +87,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -83,7 +87,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
public override bool Equals(IType other)
{
ArrayType a = other as ArrayType;
return a != null && elementType.Equals(a.elementType) && a.dimensions == dimensions;
return a != null && elementType.Equals(a.elementType) && a.dimensions == dimensions && a.nullability == nullability;
}
public override IEnumerable<IType> DirectBaseTypes {
@ -152,7 +156,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -152,7 +156,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
if (e == elementType)
return this;
else
return new ArrayType(compilation, e, dimensions);
return new ArrayType(compilation, e, dimensions, nullability);
}
}

3
ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs

@ -89,6 +89,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -89,6 +89,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// <summary>
/// Nullability of the reference type constraint. (e.g. "where T : class?").
///
/// Note that the nullability of a use of the type parameter may differ from this.
/// E.g. "T? GetNull&lt;T&gt;() where T : class => null;"
/// </summary>
Nullability NullabilityConstraint { get; }
}

6
ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs

@ -193,14 +193,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -193,14 +193,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
bool IType.IsByRefLike => false;
public Nullability Nullability => Nullability.Oblivious;
Nullability IType.Nullability => NullabilityConstraint;
public IType ChangeNullability(Nullability nullability)
{
if (nullability == Nullability.Oblivious)
if (nullability == NullabilityConstraint)
return this;
else
return new NullabilityAnnotatedType(this, nullability);
return new NullabilityAnnotatedTypeParameter(this, nullability);
}
IType IType.DeclaringType {

56
ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs

@ -1,22 +1,30 @@ @@ -1,22 +1,30 @@
using System.Diagnostics;
using System.Collections.Generic;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
public sealed class NullabilityAnnotatedType : DecoratedType, IType
/// <summary>
/// A decorator that annotates the nullability status for a type.
/// Note: ArrayType does not use a decorator, but has direct support for nullability.
/// </summary>
public class NullabilityAnnotatedType : DecoratedType, IType
{
readonly Nullability nullability;
internal NullabilityAnnotatedType(IType type, Nullability nullability)
: base(type)
{
Debug.Assert(nullability != Nullability.Oblivious);
Debug.Assert(!(type is ParameterizedType || type is NullabilityAnnotatedType));
Debug.Assert(nullability != type.Nullability);
// 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
|| (type is ITypeParameter && this is ITypeParameter));
this.nullability = nullability;
}
public Nullability Nullability => nullability;
public IType ElementType => baseType;
public IType TypeWithoutAnnotation => baseType;
public override IType AcceptVisitor(TypeVisitor visitor)
{
@ -42,14 +50,48 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -42,14 +50,48 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
IType newBase = baseType.AcceptVisitor(visitor);
if (newBase != baseType)
return new NullabilityAnnotatedType(newBase, nullability);
return newBase.ChangeNullability(nullability);
else
return this;
}
public override string ToString()
{
return baseType.ToString() + (nullability == Nullability.Nullable ? "?" : "!");
switch (nullability) {
case Nullability.Nullable:
return $"{baseType.ToString()}?";
case Nullability.NotNullable:
return $"{baseType.ToString()}!";
default:
Debug.Assert(nullability == Nullability.Oblivious);
return $"{baseType.ToString()}~";
}
}
}
public sealed class NullabilityAnnotatedTypeParameter : NullabilityAnnotatedType, ITypeParameter
{
readonly new ITypeParameter baseType;
internal NullabilityAnnotatedTypeParameter(ITypeParameter type, Nullability nullability)
: base(type, nullability)
{
this.baseType = type;
}
SymbolKind ITypeParameter.OwnerType => baseType.OwnerType;
IEntity ITypeParameter.Owner => baseType.Owner;
int ITypeParameter.Index => baseType.Index;
string ITypeParameter.Name => baseType.Name;
string ISymbol.Name => baseType.Name;
VarianceModifier ITypeParameter.Variance => baseType.Variance;
IType ITypeParameter.EffectiveBaseClass => baseType.EffectiveBaseClass;
IReadOnlyCollection<IType> ITypeParameter.EffectiveInterfaceSet => baseType.EffectiveInterfaceSet;
bool ITypeParameter.HasDefaultConstructorConstraint => baseType.HasDefaultConstructorConstraint;
bool ITypeParameter.HasReferenceTypeConstraint => baseType.HasReferenceTypeConstraint;
bool ITypeParameter.HasValueTypeConstraint => baseType.HasValueTypeConstraint;
Nullability ITypeParameter.NullabilityConstraint => baseType.NullabilityConstraint;
SymbolKind ISymbol.SymbolKind => SymbolKind.TypeParameter;
IEnumerable<IAttribute> ITypeParameter.GetAttributes() => baseType.GetAttributes();
}
}

10
ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs

@ -72,11 +72,19 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -72,11 +72,19 @@ namespace ICSharpCode.Decompiler.TypeSystem
public override IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type)
{
if (RemoveNullability)
return type.ElementType.AcceptVisitor(this);
return base.VisitNullabilityAnnotatedType(type).ChangeNullability(Nullability.Oblivious);
else
return base.VisitNullabilityAnnotatedType(type);
}
public override IType VisitArrayType(ArrayType type)
{
if (RemoveNullability)
return base.VisitArrayType(type).ChangeNullability(Nullability.Oblivious);
else
return base.VisitArrayType(type);
}
public override IType VisitModOpt(ModifiedType type)
{
if (RemoveModOpt) {

Loading…
Cancel
Save