{
return type.GetDefinition ().GetSignatureForError ();
}
+
+ protected static bool IsNullPropagatingValid (TypeSpec type)
+ {
+ return (TypeSpec.IsReferenceType (type) && type != InternalType.NullLiteral) || type.IsNullableType;
+ }
+
+ protected static TypeSpec LiftMemberType (ResolveContext rc, TypeSpec type)
+ {
+ return TypeSpec.IsValueType (type) && !type.IsNullableType ?
+ Nullable.NullableInfo.MakeType (rc.Module, type) :
+ type;
+ }
/// <summary>
/// Resolves an expression and performs semantic analysis on it.
ec.Report.Error (1944, loc, "An expression tree cannot contain an unsafe pointer operation");
}
+ protected void Error_NullShortCircuitInsideExpressionTree (ResolveContext rc)
+ {
+ rc.Report.Error (8072, loc, "An expression tree cannot contain a null propagating operator");
+ }
+
public virtual void FlowAnalysis (FlowAnalysisContext fc)
{
}
get;
}
+ public bool NullShortCircuit { get; set; }
+
protected abstract TypeSpec DeclaringType {
get;
}
public virtual MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original)
{
- if (left != null && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
+ if (left != null && !NullShortCircuit && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
ec.Report.Warning (1720, 1, left.Location,
"Expression will always cause a `{0}'", "System.NullReferenceException");
}
return this;
}
- protected void EmitInstance (EmitContext ec, bool prepare_for_load)
+ protected InstanceEmitter EmitInstance (EmitContext ec, bool prepare_for_load)
{
- TypeSpec instance_type = InstanceExpression.Type;
- if (TypeSpec.IsValueType (instance_type)) {
- if (InstanceExpression is IMemoryLocation) {
- ((IMemoryLocation) InstanceExpression).AddressOf (ec, AddressOp.Load);
- } else {
- // Cannot release the temporary variable when its address
- // is required to be on stack for any parent
- LocalTemporary t = new LocalTemporary (instance_type);
- InstanceExpression.Emit (ec);
- t.Store (ec);
- t.AddressOf (ec, AddressOp.Store);
- }
- } else {
- InstanceExpression.Emit (ec);
-
- // Only to make verifier happy
- if (instance_type.IsGenericParameter && !(InstanceExpression is This) && TypeSpec.IsReferenceType (instance_type))
- ec.Emit (OpCodes.Box, instance_type);
- }
+ var inst = new InstanceEmitter (InstanceExpression, TypeSpec.IsValueType (InstanceExpression.Type));
+ inst.NullShortCircuit = NullShortCircuit;
+ inst.Emit (ec);
if (prepare_for_load)
ec.Emit (OpCodes.Dup);
+
+ return inst;
}
public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta);
if (IsConditionallyExcluded)
ec.Report.Error (765, loc,
"Partial methods with only a defining declaration or removed conditional methods cannot be used in an expression tree");
-
+
+ if (NullShortCircuit)
+ Error_NullShortCircuitInsideExpressionTree (ec);
+
return new TypeOfMethod (best_candidate, loc);
}
throw new NotSupportedException ();
}
- public void EmitCall (EmitContext ec, Arguments arguments)
+ public void EmitCall (EmitContext ec, Arguments arguments, bool statement)
{
var call = new CallEmitter ();
call.InstanceExpression = InstanceExpression;
- call.Emit (ec, best_candidate, arguments, loc);
+ call.NullShortCircuit = NullShortCircuit;
+ if (statement)
+ call.EmitStatement (ec, best_candidate, arguments, loc);
+ else
+ call.Emit (ec, best_candidate, arguments, loc);
}
public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl)
if (best_candidate_return.Kind == MemberKind.Void && best_candidate.IsConditionallyExcluded (ec))
Methods = Excluded;
+ if (NullShortCircuit)
+ best_candidate_return = LiftMemberType (ec, best_candidate_return);
+
return this;
}
if (!TypeSpecComparer.Equals (p_m.Parameters.Types, q_m.Parameters.Types))
return 0;
- var orig_p = p;
p = p_m.ReturnType;
var orig_q = q;
q = q_m.ReturnType;
q = q.TypeArguments[0];
p = p.TypeArguments[0];
}
- } else if (q != p) {
+ }
+
+ if (q != p) {
//
- // LAMESPEC: Lambda expression returning dynamic type has identity (better) conversion to delegate returning object type
+ // An inferred return type X exists for E in the context of that parameter list, and
+ // the conversion from X to Y1 is better than the conversion from X to Y2
//
- if (q.BuiltinType == BuiltinTypeSpec.Type.Object) {
- var am_rt = am.InferReturnType (ec, null, orig_q);
- if (am_rt != null && am_rt.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
- return 2;
- } else if (p.BuiltinType == BuiltinTypeSpec.Type.Object) {
- var am_rt = am.InferReturnType (ec, null, orig_p);
- if (am_rt != null && am_rt.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
- return 1;
+ argument_type = am.InferReturnType (ec, null, orig_q);
+ if (argument_type == null) {
+ // TODO: Can this be hit?
+ return 1;
}
+
+ if (argument_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
+ argument_type = ec.BuiltinTypes.Object;
}
+ }
- //
- // The parameters are identicial and return type is not void, use better type conversion
- // on return type to determine better one
- //
- } else {
- if (argument_type == p)
- return 1;
+ if (argument_type == p)
+ return 1;
- if (argument_type == q)
- return 2;
- }
+ if (argument_type == q)
+ return 2;
+ //
+ // The parameters are identicial and return type is not void, use better type conversion
+ // on return type to determine better one
+ //
return BetterTypeConversion (ec, p, q);
}
bool VerifyArguments (ResolveContext ec, ref Arguments args, MemberSpec member, IParametersMember pm, bool chose_params_expanded)
{
var pd = pm.Parameters;
- TypeSpec[] ptypes = ((IParametersMember) member).Parameters.Types;
+ var cpd = ((IParametersMember) member).Parameters;
+ var ptypes = cpd.Types;
Parameter.Modifier p_mod = 0;
TypeSpec pt = null;
continue;
if (p_mod != Parameter.Modifier.PARAMS) {
- p_mod = pd.FixedParameters[a_idx].ModFlags;
+ p_mod = cpd.FixedParameters [a_idx].ModFlags;
pt = ptypes[a_idx];
has_unsafe_arg |= pt.IsPointer;
public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (NullShortCircuit) {
+ Error_NullShortCircuitInsideExpressionTree (ec);
+ }
+
return CreateExpressionTree (ec, true);
}
variable_info = var.VariableInfo.GetStructFieldInfo (Name);
}
+ if (NullShortCircuit) {
+ type = LiftMemberType (ec, type);
+
+ if (InstanceExpression.IsNull)
+ return Constant.CreateConstantFromValue (type, null, loc);
+ }
+
eclass = ExprClass.Variable;
return this;
}
return null;
}
- override public Expression DoResolveLValue (ResolveContext ec, Expression right_side)
+ public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
+ if (NullShortCircuit)
+ throw new NotSupportedException ("null propagating operator assignment");
+
if (spec is FixedFieldSpec) {
// It could be much better error message but we want to be error compatible
Error_ValueAssignment (ec, right_side);
ec.Emit (OpCodes.Ldsfld, spec);
} else {
+ InstanceEmitter ie;
if (!prepared)
- EmitInstance (ec, false);
+ ie = EmitInstance (ec, false);
+ else
+ ie = new InstanceEmitter ();
// Optimization for build-in types
if (type.IsStruct && type == ec.CurrentType && InstanceExpression.Type == type) {
ec.Emit (OpCodes.Volatile);
ec.Emit (OpCodes.Ldfld, spec);
+
+ if (NullShortCircuit) {
+ ie.EmitResultLift (ec, spec.MemberType, false);
+ }
}
}
}
}
if (IsInstance) {
+ if (NullShortCircuit)
+ throw new NotImplementedException ("null operator assignment");
+
if (has_await_source)
source = source.EmitToField (ec);
public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (NullShortCircuit) {
+ Error_NullShortCircuitInsideExpressionTree (ec);
+ }
+
Arguments args;
if (IsSingleDimensionalArrayLength ()) {
args = new Arguments (1);
// Special case: length of single dimension array property is turned into ldlen
//
if (IsSingleDimensionalArrayLength ()) {
- EmitInstance (ec, false);
+ var inst = EmitInstance (ec, false);
+
ec.Emit (OpCodes.Ldlen);
ec.Emit (OpCodes.Conv_I4);
+
+ if (NullShortCircuit)
+ inst.EmitResultLift (ec, ec.BuiltinTypes.Int, false);
+
return;
}
call.InstanceExpression = InstanceExpression;
if (args == null)
call.InstanceExpressionOnStack = true;
+ if (NullShortCircuit) {
+ call.NullShortCircuit = true;
+ call.NullOperatorLabel = null_operator_label;
+ }
- call.Emit (ec, Setter, args, loc);
+ if (leave_copy)
+ call.Emit (ec, Setter, args, loc);
+ else
+ call.EmitStatement (ec, Setter, args, loc);
if (temp != null) {
temp.Emit (ec);
protected LocalTemporary temp;
protected bool emitting_compound_assignment;
protected bool has_await_arguments;
+ protected Label null_operator_label;
protected PropertyOrIndexerExpr (Location l)
{
if (expr == null)
return null;
+ if (NullShortCircuit && !ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) {
+ type = LiftMemberType (ec, type);
+ }
+
if (expr != this)
return expr.Resolve (ec);
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
+ if (NullShortCircuit)
+ throw new NotSupportedException ("null propagating operator assignment");
+
if (right_side == EmptyExpression.OutAccess) {
// TODO: best_candidate can be null at this point
INamedBlockVariable variable = null;
if (!ResolveSetter (ec))
return null;
-
+/*
+ if (NullShortCircuit && ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) {
+ var lifted_type = LiftMemberType (ec, type);
+ if (type != lifted_type) {
+ // TODO: Workaround to disable codegen for now
+ return Nullable.Wrap.Create (this, lifted_type);
+ }
+ }
+*/
return this;
}
public virtual void Emit (EmitContext ec, bool leave_copy)
{
var call = new CallEmitter ();
+ call.NullShortCircuit = NullShortCircuit;
call.InstanceExpression = InstanceExpression;
if (has_await_arguments)
call.HasAwaitArguments = true;
has_await_arguments = true;
}
+ if (NullShortCircuit && emitting_compound_assignment)
+ null_operator_label = call.NullOperatorLabel;
+
if (leave_copy) {
ec.Emit (OpCodes.Dup);
temp = new LocalTemporary (Type);
var call = new CallEmitter ();
call.InstanceExpression = InstanceExpression;
- call.Emit (ec, op, args, loc);
+ call.NullShortCircuit = NullShortCircuit;
+ call.EmitStatement (ec, op, args, loc);
}
#endregion