diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
index 69e1721aa..ca5e5f4c8 100644
--- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
+++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
@@ -751,6 +751,7 @@ namespace ICSharpCode.Decompiler.Ast
return new ParenthesizedExpression(arg1);
case ILCode.AddressOf:
return MakeRef(arg1);
+ case ILCode.ValueOf: return arg1;
default:
throw new Exception("Unknown OpCode: " + byteCode.Code);
}
diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs
index 46a269259..8216a1b60 100644
--- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs
+++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs
@@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.ILAst
InitArray, // Array Initializer
///
- /// Defines a barrier between the parent opcode and argument opcode that prevents combining them
+ /// Defines a barrier between the parent expression and the argument expression that prevents combining them
///
Wrap,
@@ -316,7 +316,12 @@ namespace ICSharpCode.Decompiler.ILAst
/// Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays.
/// Also used when inlining a method call on a value type: "stloc(v, ...); call(M, ldloca(v));" becomes "call(M, AddressOf(...))"
///
- AddressOf
+ AddressOf,
+ /// Simulates getting the value of the nullable argument in comparisons involving nullable values
+ ///
+ /// For example "stloc(v1, ...); stloc(v2, ...); logicand(ceq(call(Nullable`1::GetValueOrDefault, ldloca(v1)), ldloc(v2)), callgetter(Nullable`1::get_HasValue, ldloca(v1)))" becomes "ceq(ValueOf(...), ...)"
+ ///
+ ValueOf,
}
public static class ILCodeUtil
diff --git a/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/ICSharpCode.Decompiler/ILAst/ILInlining.cs
index d839f41ef..31519b75e 100644
--- a/ICSharpCode.Decompiler/ILAst/ILInlining.cs
+++ b/ICSharpCode.Decompiler/ILAst/ILInlining.cs
@@ -406,7 +406,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
///
- /// Determines whether it is save to move 'expressionBeingMoved' past 'expr'
+ /// Determines whether it is safe to move 'expressionBeingMoved' past 'expr'
///
bool IsSafeForInlineOver(ILExpression expr, ILExpression expressionBeingMoved)
{
@@ -427,6 +427,8 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Ldflda:
case ILCode.Ldsflda:
case ILCode.Ldelema:
+ case ILCode.AddressOf:
+ case ILCode.ValueOf:
// address-loading instructions are safe if their arguments are safe
foreach (ILExpression arg in expr.Arguments) {
if (!IsSafeForInlineOver(arg, expressionBeingMoved))
diff --git a/ICSharpCode.Decompiler/ILAst/NullableOperators.cs b/ICSharpCode.Decompiler/ILAst/NullableOperators.cs
index e830484bf..400a853f5 100644
--- a/ICSharpCode.Decompiler/ILAst/NullableOperators.cs
+++ b/ICSharpCode.Decompiler/ILAst/NullableOperators.cs
@@ -217,7 +217,10 @@ namespace ICSharpCode.Decompiler.ILAst
public override ILExpression BuildNew(ref PatternMatcher pm, ILExpression[] args)
{
- return new ILExpression(this.code, this.b ? pm.B : pm.A, args);
+ var v = this.b ? pm.B : pm.A;
+ var e = new ILExpression(ILCode.Ldloc, v, args);
+ if (v.Type.Name == "Nullable`1" && v.Type.Namespace == "System") e = new ILExpression(ILCode.ValueOf, null, e);
+ return e;
}
}
diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
index 819b5f7ae..9da8e0021 100644
--- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
+++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
@@ -487,6 +487,15 @@ namespace ICSharpCode.Decompiler.ILAst
TypeReference t = InferTypeForExpression(expr.Arguments[0], UnpackPointer(expectedType));
return t != null ? new ByReferenceType(t) : null;
}
+ case ILCode.ValueOf: {
+ GenericInstanceType t = null;
+ if (expectedType != null) {
+ t = new GenericInstanceType(new TypeReference("System", "Nullable`1", module, module.TypeSystem.Corlib));
+ t.GenericArguments.Add(expectedType);
+ }
+ t = InferTypeForExpression(expr.Arguments[0], t) as GenericInstanceType;
+ return t == null || t.Name != "Nullable`1" || t.Namespace != "System" ? null : t.GenericArguments[0];
+ }
#endregion
#region Arithmetic instructions
case ILCode.Not: // bitwise complement