}
}
+ public ConditionalAccessContext ConditionalAccess { get; set; }
+
public TypeSpec CurrentType {
get { return member_context.CurrentType; }
}
#endif
}
+ public void CloseConditionalAccess (TypeSpec type)
+ {
+ if (type != null)
+ Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type));
+
+ MarkLabel (ConditionalAccess.EndLabel);
+ ConditionalAccess = null;
+ }
+
//
// Creates a nested container in this context for all dynamic compiler generated stuff
//
}
}
+ public class ConditionalAccessContext
+ {
+ public ConditionalAccessContext (TypeSpec type, Label endLabel)
+ {
+ Type = type;
+ EndLabel = endLabel;
+ }
+
+ public bool Statement { get; set; }
+ public Label EndLabel { get; private set; }
+ public TypeSpec Type { get; private set; }
+ }
+
struct CallEmitter
{
public Expression InstanceExpression;
//
public bool HasAwaitArguments;
- public bool NullShortCircuit;
+ public bool ConditionalAccess;
//
// When dealing with await arguments the original arguments are converted
OpCode call_op;
LocalTemporary lt = null;
- InstanceEmitter ie = new InstanceEmitter ();
if (method.IsStatic) {
call_op = OpCodes.Call;
} else {
- if (IsVirtualCallRequired (InstanceExpression, method)) {
- call_op = OpCodes.Callvirt;
- } else {
- call_op = OpCodes.Call;
- }
+ call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call;
if (HasAwaitArguments) {
instance_copy = InstanceExpression.EmitToField (ec);
- ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+ var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
if (Arguments == null) {
ie.EmitLoad (ec);
}
} else if (!InstanceExpressionOnStack) {
- ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
- ie.NullShortCircuit = NullShortCircuit;
- ie.Emit (ec);
-
- if (NullShortCircuit) {
- NullOperatorLabel = ie.NullOperatorLabel;
- }
+ var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
+ ie.Emit (ec, ConditionalAccess);
if (DuplicateArguments) {
ec.Emit (OpCodes.Dup);
EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
if (EmittedArguments != null) {
if (instance_copy != null) {
- ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
- ie.Emit (ec);
+ var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+ ie.Emit (ec, ConditionalAccess);
if (lt != null)
lt.Release (ec);
//
if (statement && method.ReturnType.Kind != MemberKind.Void)
ec.Emit (OpCodes.Pop);
-
- if (NullShortCircuit && !DuplicateArguments) {
- ie.EmitResultLift (ec, method.ReturnType, statement);
- }
}
static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
{
readonly Expression instance;
readonly bool addressRequired;
- bool value_on_stack;
-
- public bool NullShortCircuit;
- public Label NullOperatorLabel;
public InstanceEmitter (Expression instance, bool addressLoad)
{
this.instance = instance;
this.addressRequired = addressLoad;
- NullShortCircuit = false;
- NullOperatorLabel = new Label ();
- value_on_stack = false;
}
- public void Emit (EmitContext ec)
+ public void Emit (EmitContext ec, bool conditionalAccess)
{
+ Label NullOperatorLabel;
Nullable.Unwrap unwrap;
- if (NullShortCircuit) {
+ if (conditionalAccess) {
NullOperatorLabel = ec.DefineLabel ();
unwrap = instance as Nullable.Unwrap;
} else {
+ NullOperatorLabel = new Label ();
unwrap = null;
}
if (unwrap != null) {
unwrap.Store (ec);
unwrap.EmitCheck (ec);
- ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
- unwrap.Emit (ec);
- var tmp = ec.GetTemporaryLocal (unwrap.Type);
- ec.Emit (OpCodes.Stloc, tmp);
- ec.Emit (OpCodes.Ldloca, tmp);
- ec.FreeTemporaryLocal (tmp, unwrap.Type);
- return;
+ ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+ } else {
+ EmitLoad (ec);
+
+ if (conditionalAccess) {
+ ec.Emit (OpCodes.Dup);
+ ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+ ec.Emit (OpCodes.Pop);
+ }
}
- EmitLoad (ec);
+ if (conditionalAccess) {
+ if (!ec.ConditionalAccess.Statement) {
+ if (ec.ConditionalAccess.Type.IsNullableType)
+ Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec);
+ else
+ ec.EmitNull ();
+ }
+
+ ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
+ ec.MarkLabel (NullOperatorLabel);
- if (NullShortCircuit) {
- ec.Emit (OpCodes.Dup);
- ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
+ if (unwrap != null) {
+ unwrap.Emit (ec);
+ var tmp = ec.GetTemporaryLocal (unwrap.Type);
+ ec.Emit (OpCodes.Stloc, tmp);
+ ec.Emit (OpCodes.Ldloca, tmp);
+ ec.FreeTemporaryLocal (tmp, unwrap.Type);
+ }
}
-
- value_on_stack = true;
}
public void EmitLoad (EmitContext ec)
}
}
- public void EmitResultLift (EmitContext ec, TypeSpec type, bool statement)
- {
- if (!NullShortCircuit)
- throw new InternalErrorException ();
-
- bool value_rt = TypeSpec.IsValueType (type);
- TypeSpec lifted;
- if (value_rt) {
- if (type.IsNullableType)
- lifted = type;
- else {
- lifted = Nullable.NullableInfo.MakeType (ec.Module, type);
- ec.Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (lifted));
- }
- } else {
- lifted = null;
- }
-
- var end = ec.DefineLabel ();
- if (value_on_stack || !statement) {
- ec.Emit (OpCodes.Br_S, end);
- }
-
- ec.MarkLabel (NullOperatorLabel);
-
- if (value_on_stack)
- ec.Emit (OpCodes.Pop);
-
- if (!statement) {
- if (value_rt)
- Nullable.LiftedNull.Create (lifted, Location.Null).Emit (ec);
- else
- ec.EmitNull ();
- }
-
- ec.MarkLabel (end);
- }
-
public TypeSpec GetStackType (EmitContext ec)
{
var instance_type = instance.Type;
TryWithCatchScope = 1 << 15,
+ ConditionalAccessReceiver = 1 << 16,
+
///
/// Indicates the current context is in probing mode, no errors are reported.
///
FeatureIsNotAvailable (GetLocation ($2), "null propagating operator");
var lt = (LocatedToken) $4;
- $$ = new NullMemberAccess ((Expression) $1, lt.Value, (TypeArguments) $5, lt.Location);
+ $$ = new ConditionalMemberAccess ((Expression) $1, lt.Value, (TypeArguments) $5, lt.Location);
lbag.AddLocation ($$, GetLocation ($2), GetLocation ($3));
}
| builtin_types DOT identifier_inside_body opt_type_argument_list
FeatureIsNotAvailable (GetLocation ($2), "null propagating operator");
$$ = new ElementAccess ((Expression) $1, (Arguments) $4, GetLocation ($3)) {
- NullPropagating = true
+ ConditionalAccess = true
};
lbag.AddLocation ($$, GetLocation ($2), GetLocation ($5));
//
public abstract class DelegateCreation : Expression, OverloadResolver.IErrorHandler
{
+ bool conditional_access_receiver;
protected MethodSpec constructor_method;
protected MethodGroupExpr method_group;
var invoke_method = Delegate.GetInvokeMethod (type);
+ if (!ec.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) {
+ if (method_group.HasConditionalAccess ()) {
+ conditional_access_receiver = true;
+ ec.Set (ResolveContext.Options.ConditionalAccessReceiver);
+ }
+ }
+
Arguments arguments = CreateDelegateMethodArguments (ec, invoke_method.Parameters, invoke_method.Parameters.Types, loc);
method_group = method_group.OverloadResolve (ec, ref arguments, this, OverloadResolver.Restrictions.CovariantDelegate);
+
+ if (conditional_access_receiver)
+ ec.With (ResolveContext.Options.ConditionalAccessReceiver, false);
+
if (method_group == null)
return null;
public override void Emit (EmitContext ec)
{
- InstanceEmitter ie;
+ if (conditional_access_receiver)
+ ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+
if (method_group.InstanceExpression == null) {
- ie = new InstanceEmitter ();
ec.EmitNull ();
} else {
- ie = new InstanceEmitter (method_group.InstanceExpression, false);
- ie.NullShortCircuit = method_group.NullShortCircuit;
- ie.Emit (ec);
+ var ie = new InstanceEmitter (method_group.InstanceExpression, false);
+ ie.Emit (ec, method_group.ConditionalAccess);
}
var delegate_method = method_group.BestCandidate;
ec.Emit (OpCodes.Newobj, constructor_method);
- if (method_group.NullShortCircuit) {
- ie.EmitResultLift (ec, type, false);
- }
+ if (conditional_access_receiver)
+ ec.CloseConditionalAccess (null);
}
public override void FlowAnalysis (FlowAnalysisContext fc) {
return (TypeSpec.IsReferenceType (type) && type != InternalType.NullLiteral) || type.IsNullableType;
}
+ public virtual bool HasConditionalAccess ()
+ {
+ return false;
+ }
+
protected static TypeSpec LiftMemberType (ResolveContext rc, TypeSpec type)
{
return TypeSpec.IsValueType (type) && !type.IsNullableType ?
/// </summary>
public abstract class MemberExpr : Expression, OverloadResolver.IInstanceQualifier
{
+ protected bool conditional_access_receiver;
+
//
// An instance expression associated with this member, if it's a
// non-static member
get;
}
- public bool NullShortCircuit { get; set; }
+ public bool ConditionalAccess { get; set; }
protected abstract TypeSpec DeclaringType {
get;
return InstanceExpression != null && InstanceExpression.ContainsEmitWithAwait ();
}
+ public override bool HasConditionalAccess ()
+ {
+ return ConditionalAccess || (InstanceExpression != null && InstanceExpression.HasConditionalAccess ());
+ }
+
static bool IsSameOrBaseQualifier (TypeSpec type, TypeSpec qtype)
{
do {
InstanceExpression.FlowAnalysis (fc);
}
+ protected void ResolveConditionalAccessReceiver (ResolveContext rc)
+ {
+ if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) {
+ if (HasConditionalAccess ()) {
+ conditional_access_receiver = true;
+ rc.Set (ResolveContext.Options.ConditionalAccessReceiver);
+ }
+ }
+ }
+
public bool ResolveInstanceExpression (ResolveContext rc, Expression rhs)
{
if (!ResolveInstanceExpressionCore (rc, rhs))
public virtual MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original)
{
- if (left != null && !NullShortCircuit && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
+ if (left != null && !ConditionalAccess && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
ec.Report.Warning (1720, 1, left.Location,
"Expression will always cause a `{0}'", "System.NullReferenceException");
}
return this;
}
- protected InstanceEmitter EmitInstance (EmitContext ec, bool prepare_for_load)
+ protected void EmitInstance (EmitContext ec, bool prepare_for_load)
{
var inst = new InstanceEmitter (InstanceExpression, TypeSpec.IsValueType (InstanceExpression.Type));
- inst.NullShortCircuit = NullShortCircuit;
- inst.Emit (ec);
+ inst.Emit (ec, ConditionalAccess);
if (prepare_for_load)
ec.Emit (OpCodes.Dup);
-
- return inst;
}
public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta);
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)
+ if (ConditionalAccess)
Error_NullShortCircuitInsideExpressionTree (ec);
return new TypeOfMethod (best_candidate, loc);
{
throw new NotSupportedException ();
}
-
+
public void EmitCall (EmitContext ec, Arguments arguments, bool statement)
{
var call = new CallEmitter ();
call.InstanceExpression = InstanceExpression;
- call.NullShortCircuit = NullShortCircuit;
+ call.ConditionalAccess = ConditionalAccess;
+
if (statement)
call.EmitStatement (ec, best_candidate, arguments, loc);
else
call.Emit (ec, best_candidate, arguments, loc);
}
+ public void EmitCall (EmitContext ec, Arguments arguments, TypeSpec conditionalAccessReceiver, bool statement)
+ {
+ ec.ConditionalAccess = new ConditionalAccessContext (conditionalAccessReceiver, ec.DefineLabel ()) {
+ Statement = statement
+ };
+
+ EmitCall (ec, arguments, statement);
+
+ ec.CloseConditionalAccess (!statement && best_candidate_return != conditionalAccessReceiver && conditionalAccessReceiver.IsNullableType ? conditionalAccessReceiver : null);
+ }
+
public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl)
{
ec.Report.Error (428, loc, "Cannot convert method group `{0}' to non-delegate type `{1}'. Consider using parentheses to invoke the method",
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;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
- if (NullShortCircuit) {
+ if (ConditionalAccess) {
Error_NullShortCircuitInsideExpressionTree (ec);
}
bool lvalue_instance = rhs != null && IsInstance && spec.DeclaringType.IsStruct;
if (rhs != this) {
+ ResolveConditionalAccessReceiver (ec);
+
if (ResolveInstanceExpression (ec, rhs)) {
// Resolve the field's instance expression while flow analysis is turned
// off: when accessing a field "a.b", we must check whether the field
}
DoBestMemberChecks (ec, spec);
+
+ if (conditional_access_receiver)
+ ec.With (ResolveContext.Options.ConditionalAccessReceiver, false);
}
var fb = spec as FixedFieldSpec;
variable_info = var.VariableInfo.GetStructFieldInfo (Name);
}
- if (NullShortCircuit) {
- type = LiftMemberType (ec, type);
+ if (ConditionalAccess) {
+ if (conditional_access_receiver)
+ type = LiftMemberType (ec, type);
if (InstanceExpression.IsNull)
return Constant.CreateConstantFromValue (type, null, loc);
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
- if (NullShortCircuit)
+ if (ConditionalAccess)
throw new NotSupportedException ("null propagating operator assignment");
if (spec is FixedFieldSpec) {
ec.Emit (OpCodes.Ldsfld, spec);
} else {
- InstanceEmitter ie;
- if (!prepared)
- ie = EmitInstance (ec, false);
- else
- ie = new InstanceEmitter ();
+ if (!prepared) {
+ if (conditional_access_receiver)
+ ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+
+ EmitInstance (ec, false);
+ }
// 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 (conditional_access_receiver) {
+ ec.CloseConditionalAccess (type.IsNullableType && type != spec.MemberType ? type : null);
+ }
}
if (leave_copy) {
}
if (IsInstance) {
- if (NullShortCircuit)
+ if (ConditionalAccess)
throw new NotImplementedException ("null operator assignment");
if (has_await_source)
public override Expression CreateExpressionTree (ResolveContext ec)
{
- if (NullShortCircuit) {
+ if (ConditionalAccess) {
Error_NullShortCircuitInsideExpressionTree (ec);
}
// Special case: length of single dimension array property is turned into ldlen
//
if (IsSingleDimensionalArrayLength ()) {
- var inst = EmitInstance (ec, false);
+ if (conditional_access_receiver) {
+ ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+ }
+
+ EmitInstance (ec, false);
ec.Emit (OpCodes.Ldlen);
ec.Emit (OpCodes.Conv_I4);
- if (NullShortCircuit)
- inst.EmitResultLift (ec, ec.BuiltinTypes.Int, false);
+ if (conditional_access_receiver) {
+ ec.CloseConditionalAccess (type);
+ }
return;
}
call.InstanceExpression = InstanceExpression;
if (args == null)
call.InstanceExpressionOnStack = true;
- if (NullShortCircuit) {
- call.NullShortCircuit = true;
- call.NullOperatorLabel = null_operator_label;
+
+ if (ConditionalAccess) {
+ call.ConditionalAccess = true;
}
if (leave_copy)
protected LocalTemporary temp;
protected bool emitting_compound_assignment;
protected bool has_await_arguments;
- protected Label null_operator_label;
protected PropertyOrIndexerExpr (Location l)
{
protected override Expression DoResolve (ResolveContext ec)
{
if (eclass == ExprClass.Unresolved) {
+ ResolveConditionalAccessReceiver (ec);
+
var expr = OverloadResolve (ec, null);
if (expr == null)
return null;
- if (NullShortCircuit && !ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) {
- type = LiftMemberType (ec, type);
- }
-
if (expr != this)
return expr.Resolve (ec);
+
+ if (conditional_access_receiver) {
+ type = LiftMemberType (ec, type);
+ ec.With (ResolveContext.Options.ConditionalAccessReceiver, false);
+ }
}
if (!ResolveGetter (ec))
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
- if (NullShortCircuit)
+ if (ConditionalAccess)
throw new NotSupportedException ("null propagating operator assignment");
if (right_side == EmptyExpression.OutAccess) {
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;
}
+ void EmitConditionalAccess (EmitContext ec, ref CallEmitter call, MethodSpec method, Arguments arguments)
+ {
+ ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+
+ call.Emit (ec, method, arguments, loc);
+
+ ec.CloseConditionalAccess (method.ReturnType != type && type.IsNullableType ? type : null);
+ }
+
//
// Implements the IAssignMethod interface for assignments
//
public virtual void Emit (EmitContext ec, bool leave_copy)
{
var call = new CallEmitter ();
- call.NullShortCircuit = NullShortCircuit;
+ call.ConditionalAccess = ConditionalAccess;
call.InstanceExpression = InstanceExpression;
if (has_await_arguments)
call.HasAwaitArguments = true;
else
call.DuplicateArguments = emitting_compound_assignment;
- call.Emit (ec, Getter, Arguments, loc);
+ if (conditional_access_receiver)
+ EmitConditionalAccess (ec, ref call, Getter, Arguments);
+ else
+ call.Emit (ec, Getter, Arguments, loc);
if (call.HasAwaitArguments) {
InstanceExpression = call.InstanceExpression;
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);
Arguments args = new Arguments (1);
args.Add (new Argument (source));
+ // TODO: Wrong, needs receiver
+// if (NullShortCircuit) {
+// ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+// }
+
var call = new CallEmitter ();
call.InstanceExpression = InstanceExpression;
- call.NullShortCircuit = NullShortCircuit;
+ call.ConditionalAccess = ConditionalAccess;
call.EmitStatement (ec, op, args, loc);
+
+// if (NullShortCircuit)
+// ec.CloseConditionalAccess (null);
}
#endregion
protected Arguments arguments;
protected Expression expr;
protected MethodGroupExpr mg;
+ bool conditional_access_receiver;
public Invocation (Expression expr, Arguments arguments)
{
return CreateExpressionFactoryCall (ec, "Call", args);
}
- protected override Expression DoResolve (ResolveContext ec)
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) {
+ if (expr.HasConditionalAccess ()) {
+ conditional_access_receiver = true;
+ using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) {
+ return DoResolveInvocation (rc);
+ }
+ }
+ }
+
+ return DoResolveInvocation (rc);
+ }
+
+ Expression DoResolveInvocation (ResolveContext ec)
{
Expression member_expr;
var atn = expr as ATypeNameExpression;
var method = mg.BestCandidate;
type = mg.BestCandidateReturnType;
+ if (conditional_access_receiver)
+ type = LiftMemberType (ec, type);
if (arguments == null && method.DeclaringType.BuiltinType == BuiltinTypeSpec.Type.Object && method.Name == Destructor.MetadataName) {
if (mg.IsBase)
return mg.GetSignatureForError ();
}
+ public override bool HasConditionalAccess ()
+ {
+ return expr.HasConditionalAccess ();
+ }
+
//
// If a member is a method or event, or if it is a constant, field or property of either a delegate type
// or the type dynamic, then the member is invocable
if (mg.IsConditionallyExcluded)
return;
- mg.EmitCall (ec, arguments, false);
+ if (conditional_access_receiver)
+ mg.EmitCall (ec, arguments, type, false);
+ else
+ mg.EmitCall (ec, arguments, false);
}
public override void EmitStatement (EmitContext ec)
if (mg.IsConditionallyExcluded)
return;
- mg.EmitCall (ec, arguments, true);
+ if (conditional_access_receiver)
+ mg.EmitCall (ec, arguments, type, true);
+ else
+ mg.EmitCall (ec, arguments, true);
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
return alias + "::" + name;
}
+ public override bool HasConditionalAccess ()
+ {
+ return false;
+ }
+
public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions)
{
if ((restrictions & MemberLookupRestrictions.InvocableOnly) != 0) {
expr.Error_OperatorCannotBeApplied (rc, loc, ".", type);
}
+ public override bool HasConditionalAccess ()
+ {
+ return expr.HasConditionalAccess ();
+ }
+
public static bool IsValidDotExpression (TypeSpec type)
{
const MemberKind dot_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.Delegate | MemberKind.Enum |
return new DynamicMemberBinder (Name, args, loc);
}
- if (this is NullMemberAccess) {
+ if (this is ConditionalMemberAccess) {
if (!IsNullPropagatingValid (expr.Type)) {
expr.Error_OperatorCannotBeApplied (rc, loc, "?", expr.Type);
return null;
sn = null;
}
- if (this is NullMemberAccess) {
- me.NullShortCircuit = true;
+ if (this is ConditionalMemberAccess) {
+ me.ConditionalAccess = true;
}
me = me.ResolveMemberAccess (rc, expr, sn);
}
}
- public class NullMemberAccess : MemberAccess
+ public class ConditionalMemberAccess : MemberAccess
{
- public NullMemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
+ public ConditionalMemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
: base (expr, identifier, args, loc)
{
}
+
+ public override bool HasConditionalAccess ()
+ {
+ return true;
+ }
}
/// <summary>
this.Arguments = args;
}
- public bool NullPropagating { get; set; }
+ public bool ConditionalAccess { get; set; }
public override Location StartLocation {
get {
// We perform some simple tests, and then to "split" the emit and store
// code we create an instance of a different class, and return that.
//
- Expression CreateAccessExpression (ResolveContext ec)
+ Expression CreateAccessExpression (ResolveContext ec, bool conditionalAccessReceiver)
{
- if (NullPropagating && !IsNullPropagatingValid (type)) {
+ Expr = Expr.Resolve (ec);
+ if (Expr == null)
+ return null;
+
+ type = Expr.Type;
+
+ if (ConditionalAccess && !IsNullPropagatingValid (type)) {
Error_OperatorCannotBeApplied (ec, loc, "?", type);
return null;
}
if (type.IsArray)
return new ArrayAccess (this, loc) {
- NullShortCircuit = NullPropagating
+ ConditionalAccess = ConditionalAccess,
+ ConditionalAccessReceiver = conditionalAccessReceiver
};
if (type.IsPointer)
var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false);
if (indexers != null || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
- return new IndexerExpr (indexers, type, this) {
- NullShortCircuit = NullPropagating
+ var indexer = new IndexerExpr (indexers, type, this) {
+ ConditionalAccess = ConditionalAccess
};
+
+ if (conditionalAccessReceiver)
+ indexer.SetConditionalAccessReceiver ();
+
+ return indexer;
}
if (type != InternalType.ErrorType) {
return CreateExpressionFactoryCall (ec, "ArrayIndex", args);
}
+ public override bool HasConditionalAccess ()
+ {
+ return ConditionalAccess || Expr.HasConditionalAccess ();
+ }
+
Expression MakePointerAccess (ResolveContext rc, TypeSpec type)
{
if (Arguments.Count != 1){
return new Indirection (p, loc);
}
- protected override Expression DoResolve (ResolveContext ec)
+ protected override Expression DoResolve (ResolveContext rc)
{
- Expr = Expr.Resolve (ec);
- if (Expr == null)
- return null;
+ Expression expr;
+ if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) {
+ if (HasConditionalAccess ()) {
+ using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) {
+ expr = CreateAccessExpression (rc, true);
+ if (expr == null)
+ return null;
- type = Expr.Type;
+ return expr.Resolve (rc);
+ }
+ }
+ }
- // TODO: Create 1 result for Resolve and ResolveLValue ?
- var res = CreateAccessExpression (ec);
- if (res == null)
+ expr = CreateAccessExpression (rc, false);
+ if (expr == null)
return null;
- return res.Resolve (ec);
+ return expr.Resolve (rc);
}
public override Expression DoResolveLValue (ResolveContext ec, Expression rhs)
{
- Expr = Expr.Resolve (ec);
- if (Expr == null)
- return null;
-
- type = Expr.Type;
-
- var res = CreateAccessExpression (ec);
+ var res = CreateAccessExpression (ec, false);
if (res == null)
return null;
loc = l;
}
- public bool NullShortCircuit { get; set; }
+ public bool ConditionalAccess { get; set; }
+
+ public bool ConditionalAccessReceiver { get; set; }
public void AddressOf (EmitContext ec, AddressOp mode)
{
public override Expression CreateExpressionTree (ResolveContext ec)
{
- if (NullShortCircuit)
+ if (ConditionalAccess)
Error_NullShortCircuitInsideExpressionTree (ec);
return ea.CreateExpressionTree (ec);
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
- if (NullShortCircuit)
+ if (ConditionalAccess)
throw new NotSupportedException ("null propagating operator assignment");
return DoResolve (ec);
UnsafeError (ec, ea.Location);
}
- if (NullShortCircuit)
+ if (ConditionalAccessReceiver)
type = LiftMemberType (ec, type);
foreach (Argument a in ea.Arguments) {
//
// Load the array arguments into the stack.
//
- InstanceEmitter LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait)
+ void LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait)
{
- InstanceEmitter ie;
if (prepareAwait) {
- ie = new InstanceEmitter ();
ea.Expr = ea.Expr.EmitToField (ec);
} else {
- ie = new InstanceEmitter (ea.Expr, false);
- ie.NullShortCircuit = NullShortCircuit;
- ie.Emit (ec);
+ var ie = new InstanceEmitter (ea.Expr, false);
+ ie.Emit (ec, ConditionalAccess);
if (duplicateArguments) {
ec.Emit (OpCodes.Dup);
var dup_args = ea.Arguments.Emit (ec, duplicateArguments, prepareAwait);
if (dup_args != null)
ea.Arguments = dup_args;
-
- return ie;
}
public void Emit (EmitContext ec, bool leave_copy)
LoadInstanceAndArguments (ec, false, true);
}
+ if (ConditionalAccessReceiver)
+ ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+
var ac = (ArrayContainer) ea.Expr.Type;
- var inst = LoadInstanceAndArguments (ec, false, false);
+ LoadInstanceAndArguments (ec, false, false);
ec.EmitArrayLoad (ac);
- if (NullShortCircuit)
- inst.EmitResultLift (ec, ((ArrayContainer) ea.Expr.Type).Element, false);
+ if (ConditionalAccessReceiver)
+ ec.CloseConditionalAccess (type.IsNullableType && type != ac.Element ? type : null);
}
if (leave_copy) {
public override Expression CreateExpressionTree (ResolveContext ec)
{
- if (NullShortCircuit) {
+ if (ConditionalAccess) {
Error_NullShortCircuitInsideExpressionTree (ec);
}
target.arguments = arguments.Clone (clonectx);
}
+ public void SetConditionalAccessReceiver ()
+ {
+ conditional_access_receiver = true;
+ }
+
public override void SetTypeArguments (ResolveContext ec, TypeArguments ta)
{
Error_TypeArgumentsCannotBeUsed (ec, "indexer", GetSignatureForError (), loc);
if (t3 != null)
return 3;
+ var t4 = k?.GetLength (0).ToString () ?? "N";
+ if (t4 != "N")
+ return 4;
+
+ var t5 = k?.Length.ToString () ?? "N";
+ if (t5 != "N")
+ return 5;
+
k = new int[] { 3 };
var t11 = k?.ToString ();
if (t11.GetType () != typeof (string))
class CI
{
- public long Field;
- public sbyte? FieldNullable;
- public object FieldReference;
+ public long Field;
+ public sbyte? FieldNullable;
+ public object FieldReference;
public int Prop { get; set; }
- public byte? PropNullable { get; set; }
- public string PropReference { get; set; }
+ public byte? PropNullable { get; set; }
+ public object PropReference { get; set; }
- public event Action ev1;
+ public event Action ev1;
}
class C
{
- static int TestProperty ()
- {
- CI ci = null;
- var m1 = ci?.Prop;
- var m2 = ci?.PropNullable;
- var m3 = ci?.PropReference;
+ static int TestProperty ()
+ {
+ CI ci = null;
+
+ var m1 = ci?.Prop;
+ var m2 = ci?.PropNullable;
+ var m3 = ci?.PropReference;
+
+ var m4 = ci?.Prop.ToString () ?? "N";
+ if (m4 != "N")
+ return 1;
+
+ var m5 = ci?.PropNullable.ToString () ?? "N";
+ if (m5 != "N")
+ return 2;
+
+ var m6 = ci?.PropReference.ToString () ?? "N";
+ if (m6 != "N")
+ return 3;
// ci?.Prop = 6;
- ci = new CI ();
- m1 = ci?.Prop;
- m2 = ci?.PropNullable;
- m3 = ci?.PropReference;
+ ci = new CI ();
+ m1 = ci?.Prop;
+ m2 = ci?.PropNullable;
+ m3 = ci?.PropReference;
// ci?.Prop = 5;
// if (ci.Prop != 5)
// var pp1 = ci?.Prop = 4;
// var pp2 = ci?.Prop += 4;
- return 0;
- }
+ return 0;
+ }
- static int TestField ()
- {
- CI ci = null;
- var m1 = ci?.Field;
- var m2 = ci?.FieldNullable;
- var m3 = ci?.FieldReference;
+ static int TestField ()
+ {
+ CI ci = null;
+ var m1 = ci?.Field;
+ var m2 = ci?.FieldNullable;
+ var m3 = ci?.FieldReference;
+ var m4 = ci?.Field.ToString () ?? "N";
+ if (m4 != "N")
+ return 1;
+
+ var m5 = ci?.FieldNullable.ToString () ?? "N";
+ if (m5 != "N")
+ return 2;
+
+ var m6 = ci?.FieldReference.ToString () ?? "N";
+ if (m6 != "N")
+ return 3;
// ci?.Field = 6;
- ci = new CI ();
- m1 = ci?.Field;
- m2 = ci?.FieldNullable;
- m3 = ci?.FieldReference;
+ ci = new CI ();
+ m1 = ci?.Field;
+ m2 = ci?.FieldNullable;
+ m3 = ci?.FieldReference;
// ci?.Field = 5;
// if (ci.Field != 5)
// var pp1 = ci?.Field = 4;
// var pp2 = ci?.Field += 4;
- return 0;
- }
-
- static int TestEvent ()
- {
- CI ci = null;
- ci?.ev1 += null;
-
- ci = new CI ();
- ci?.ev1 += null;
-
- return 0;
- }
-
- static int Main ()
- {
- int res;
-
- res = TestProperty ();
- if (res != 0)
- return 10 + res;
-
- res = TestField ();
- if (res != 0)
- return 20 + res;
-
- res = TestEvent ();
- if (res != 0)
- return 30 + res;
-
- Console.WriteLine ("ok");
- return 0;
- }
+ return 0;
+ }
+/*
+ static int TestEvent ()
+ {
+ CI ci = null;
+ ci?.ev1 += null;
+
+ ci = new CI ();
+ ci?.ev1 += null;
+
+ return 0;
+ }
+*/
+ static int Main ()
+ {
+ int res;
+
+ res = TestProperty ();
+ if (res != 0)
+ return 10 + res;
+
+ res = TestField ();
+ if (res != 0)
+ return 20 + res;
+
+// res = TestEvent ();
+// if (res != 0)
+// return 30 + res;
+
+ Console.WriteLine ("ok");
+ return 0;
+ }
}
\ No newline at end of file
if (v2 != null)
return 2;
+ var v3 = arr? [0].GetHashCode () ?? 724;
+ if (v3 != 724)
+ return 3;
+
// TODO: Disabled for now?
// arr? [0] += 2;
return 0;
if (v2 != null)
return 2;
+ var v3 = ci? [0].GetHashCode () ?? 724;
+ if (v3 != 724)
+ return 3;
+
// TODO: Disabled for now?
// ci? [0] += 3;
return 0;
--- /dev/null
+public class C
+{
+ static int Main ()
+ {
+ string x = null;
+ var t1 = x?.ToString ().ToString ().ToString () ?? "t1";
+ if (t1 != "t1")
+ return 1;
+
+ var t2 = x?.ToString ().ToString ()?.ToString () ?? "t2";
+ if (t2 != "t2")
+ return 2;
+
+ var t3 = x?.ToString ()?.ToString ()?.ToString () ?? "t3";
+ if (t3 != "t3")
+ return 3;
+
+ var t4 = x?.ToString ().GetHashCode () ?? 9;
+ if (t4 != 9)
+ return 4;
+
+ var t5 = x?.ToString ()?.GetHashCode () ?? 8;
+ if (t5 != 8)
+ return 5;
+
+ return 0;
+ }
+}
\ No newline at end of file
<size>22</size>\r
</method>\r
<method name="Int32 TestArray()" attrs="145">\r
- <size>349</size>\r
+ <size>485</size>\r
</method>\r
<method name="Int32 TestReferenceType()" attrs="145">\r
<size>231</size>\r
<size>58</size>\r
</method>\r
<method name="Int32 TestNullable()" attrs="145">\r
- <size>384</size>\r
+ <size>386</size>\r
</method>\r
<method name="Int32 Main()" attrs="145">\r
<size>120</size>\r
<method name="Void set_PropNullable(System.Nullable`1[System.Byte])" attrs="2182">\r
<size>8</size>\r
</method>\r
- <method name="System.String get_PropReference()" attrs="2182">\r
- <size>14</size>\r
- </method>\r
- <method name="Void set_PropReference(System.String)" attrs="2182">\r
- <size>8</size>\r
- </method>\r
<method name="Void add_ev1(System.Action)" attrs="2182">\r
<size>42</size>\r
</method>\r
</type>\r
<type name="C">\r
<method name="Int32 Main()" attrs="145">\r
- <size>86</size>\r
+ <size>64</size>\r
</method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
</method>\r
<method name="Int32 TestProperty()" attrs="145">\r
- <size>168</size>\r
+ <size>368</size>\r
</method>\r
<method name="Int32 TestField()" attrs="145">\r
- <size>168</size>\r
+ <size>360</size>\r
</method>\r
- <method name="Int32 TestEvent()" attrs="145">\r
- <size>50</size>\r
+ </type>\r
+ <type name="CI">\r
+ <method name="System.Object get_PropReference()" attrs="2182">\r
+ <size>14</size>\r
+ </method>\r
+ <method name="Void set_PropReference(System.Object)" attrs="2182">\r
+ <size>8</size>\r
</method>\r
</type>\r
</test>\r
</type>\r
<type name="C">\r
<method name="Int32 TestArrayAccess()" attrs="145">\r
- <size>114</size>\r
+ <size>208</size>\r
</method>\r
<method name="Int32 TestIndexerAccess()" attrs="145">\r
- <size>93</size>\r
+ <size>191</size>\r
</method>\r
<method name="Int32 Main()" attrs="145">\r
<size>64</size>\r
</method>\r
</type>\r
</test>\r
+ <test name="test-null-operator-06.cs">\r
+ <type name="C">\r
+ <method name="Int32 Main()" attrs="145">\r
+ <size>409</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
<test name="test-partial-01.cs">\r
<type name="Foo.Hello">\r
<method name="Void .ctor()" attrs="6278">\r