// This is used if the expression should be resolved as a type or namespace name.
// the default implementation fails.
//
- public virtual TypeSpec ResolveAsType (IMemberContext mc)
+ public virtual TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false)
{
- ResolveContext ec = new ResolveContext (mc);
- Expression e = Resolve (ec);
+ var rc = mc as ResolveContext ?? new ResolveContext (mc);
+ Expression e = Resolve (rc);
if (e != null)
- e.Error_UnexpectedKind (ec, ResolveFlags.Type, loc);
+ e.Error_UnexpectedKind (rc, ResolveFlags.Type, loc);
return null;
}
{
return type.GetDefinition ().GetSignatureForError ();
}
+
+ public static bool IsNeverNull (Expression expr)
+ {
+ if (expr is This || expr is New || expr is ArrayCreation || expr is DelegateCreation || expr is ConditionalMemberAccess)
+ return true;
+
+ var c = expr as Constant;
+ if (c != null)
+ return !c.IsNull;
+
+ var tc = expr as TypeCast;
+ if (tc != null)
+ return IsNeverNull (tc.Child);
+
+ return false;
+ }
+
+ protected static bool IsNullPropagatingValid (TypeSpec type)
+ {
+ switch (type.Kind) {
+ case MemberKind.Struct:
+ return type.IsNullableType;
+ case MemberKind.Enum:
+ case MemberKind.Void:
+ case MemberKind.PointerType:
+ return false;
+ case MemberKind.InternalCompilerType:
+ return type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
+ case MemberKind.TypeParameter:
+ return !((TypeParameterSpec) type).IsValueType;
+ default:
+ return true;
+ }
+ }
+
+ public virtual bool HasConditionalAccess ()
+ {
+ return false;
+ }
+
+ 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.
if (ctors == null) {
switch (type.Kind) {
case MemberKind.Struct:
+ // Every struct has implicit default constructor if not provided by user
+ if (args == null)
+ return null;
+
rc.Report.SymbolRelatedToPreviousError (type);
// Report meaningful error for struct as they always have default ctor in C# context
OverloadResolver.Error_ConstructorMismatch (rc, type, args == null ? 0 : args.Count, loc);
break;
case MemberKind.MissingType:
case MemberKind.InternalCompilerType:
+// LAMESPEC: dynamic is not really object
+// if (type.BuiltinType == BuiltinTypeSpec.Type.Object)
+// goto default;
break;
default:
rc.Report.SymbolRelatedToPreviousError (type);
return null;
}
+ if (args == null && type.IsStruct) {
+ bool includes_empty = false;
+ foreach (MethodSpec ctor in ctors) {
+ if (ctor.Parameters.IsEmpty) {
+ includes_empty = true;
+ }
+ }
+
+ if (!includes_empty)
+ return null;
+ }
+
var r = new OverloadResolver (ctors, OverloadResolver.Restrictions.NoBaseMembers, loc);
if (!rc.HasSet (ResolveContext.Options.BaseInitializer)) {
r.InstanceQualifier = new ConstructorInstanceQualifier (type);
None = 0,
InvocableOnly = 1,
ExactArity = 1 << 2,
- ReadAccess = 1 << 3
+ ReadAccess = 1 << 3,
+ EmptyArguments = 1 << 4,
+ IgnoreArity = 1 << 5,
+ IgnoreAmbiguity = 1 << 6
}
//
}
if (non_method != null) {
- if (ambig_non_method != null && rc != null) {
+ if (ambig_non_method != null && rc != null && (restrictions & MemberLookupRestrictions.IgnoreAmbiguity) == 0) {
var report = rc.Module.Compiler.Report;
report.SymbolRelatedToPreviousError (non_method);
report.SymbolRelatedToPreviousError (ambig_non_method);
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)
{
}
+ //
+ // Special version of flow analysis for expressions which can return different
+ // on-true and on-false result. Used by &&, ||, ?: expressions
+ //
+ public virtual void FlowAnalysisConditional (FlowAnalysisContext fc)
+ {
+ FlowAnalysis (fc);
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
+ }
+
/// <summary>
/// Returns an expression that can be used to invoke operator true
/// on the expression if it exists.
static Expression GetOperatorTrueOrFalse (ResolveContext ec, Expression e, bool is_true, Location loc)
{
var op = is_true ? Operator.OpType.True : Operator.OpType.False;
- var methods = MemberCache.GetUserOperator (e.type, op, false);
+ var type = e.type;
+ if (type.IsNullableType)
+ type = Nullable.NullableInfo.GetUnderlyingType (type);
+
+ var methods = MemberCache.GetUserOperator (type, op, false);
if (methods == null)
return null;
(targs == null || targs.Equals (atne.targs));
}
+ protected void Error_OpenGenericTypeIsNotAllowed (IMemberContext mc)
+ {
+ mc.Module.Compiler.Report.Error (7003, Location, "Unbound generic name is not valid in this context");
+ }
+
public override int GetHashCode ()
{
return Name.GetHashCode ();
return SimpleNameResolve (ec, right_side);
}
+ public void Error_NameDoesNotExist (ResolveContext rc)
+ {
+ rc.Report.Error (103, loc, "The name `{0}' does not exist in the current context", Name);
+ }
+
protected virtual void Error_TypeOrNamespaceNotFound (IMemberContext ctx)
{
if (ctx.CurrentType != null) {
}
}
- public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc)
+ public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments)
{
FullNamedExpression fne = mc.LookupNamespaceOrType (Name, Arity, LookupMode.Normal, loc);
return ct;
}
+ if (!allowUnboundTypeArguments)
+ Error_OpenGenericTypeIsNotAllowed (mc);
+
return new GenericOpenTypeExpr (fne.Type, loc);
}
rc.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", Name);
}
- return ResolveAsTypeOrNamespace (rc);
+ return ResolveAsTypeOrNamespace (rc, false);
+ }
+ }
+
+ var mg = NamespaceContainer.LookupStaticUsings (rc, Name, Arity, loc);
+ if (mg != null) {
+ if (Arity > 0) {
+ targs.Resolve (rc);
+ mg.SetTypeArguments (rc, targs);
}
+ return mg;
}
+ if (Name == "nameof")
+ return new NameOf (this);
+
if (errorMode) {
if (variable_found) {
rc.Report.Error (841, loc, "A local variable `{0}' cannot be used before it is declared", Name);
ct = ct.DeclaringType;
} while (ct != null);
- } else {
- var cos = rc.CurrentMemberDefinition.Parent as ClassOrStruct;
- if (cos != null && cos.PrimaryConstructorParameters != null) {
- foreach (var p in cos.PrimaryConstructorParameters.FixedParameters) {
- if (p.Name == Name) {
- rc.Report.Error (9007, loc, "Primary constructor parameter `{0}' is not available in this context when using ref or out modifier",
- Name);
- return null;
- }
- }
- }
}
if ((restrictions & MemberLookupRestrictions.InvocableOnly) == 0) {
e = rc.LookupNamespaceOrType (Name, -System.Math.Max (1, Arity), LookupMode.Probing, loc);
if (e != null) {
- if (e.Type.Arity != Arity) {
+ if (e.Type.Arity != Arity && (restrictions & MemberLookupRestrictions.IgnoreArity) == 0) {
Error_TypeArgumentsCannotBeUsed (rc, e.Type, loc);
return e;
}
}
}
- rc.Report.Error (103, loc, "The name `{0}' does not exist in the current context", Name);
+ Error_NameDoesNotExist (rc);
}
return ErrorExpression.Instance;
throw new NotSupportedException ("ET");
}
- public abstract FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc);
+ public abstract FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments);
//
// This is used to resolve the expression as a type, a null
// value will be returned if the expression is not a type
// reference
//
- public override TypeSpec ResolveAsType (IMemberContext mc)
+ public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false)
{
- FullNamedExpression fne = ResolveAsTypeOrNamespace (mc);
+ FullNamedExpression fne = ResolveAsTypeOrNamespace (mc, allowUnboundTypeArguments);
if (fne == null)
return null;
/// </summary>
public abstract class TypeExpr : FullNamedExpression
{
- public sealed override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc)
+ public sealed override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments)
{
ResolveAsType (mc);
return this;
loc = l;
}
- public sealed override TypeSpec ResolveAsType (IMemberContext ec)
+ public sealed override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false)
{
return type;
}
throw new NotImplementedException ();
}
- public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc)
+ public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments)
{
return this;
}
/// </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 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 {
public override void FlowAnalysis (FlowAnalysisContext fc)
{
- if (InstanceExpression != null)
+ if (InstanceExpression != null) {
InstanceExpression.FlowAnalysis (fc);
+
+ if (ConditionalAccess) {
+ fc.BranchConditionalAccessDefiniteAssignment ();
+ }
+ }
+ }
+
+ 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)
//
// Check intermediate value modification which won't have any effect
//
- if (rhs != null && InstanceExpression.Type.IsStruct) {
+ if (rhs != null && TypeSpec.IsValueType (InstanceExpression.Type)) {
var fexpr = InstanceExpression as FieldExpr;
if (fexpr != null) {
if (!fexpr.Spec.IsReadOnly || rc.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.ConstructorScope))
public virtual MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original)
{
- if (left != null && 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");
}
protected void 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.Emit (ec, ConditionalAccess);
if (prepare_for_load)
ec.Emit (OpCodes.Dup);
get { return true; }
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (ConditionalAccess) {
+ fc.BranchConditionalAccessDefiniteAssignment ();
+ }
+ }
+
//
// For extension methodgroup we are not looking for base members but parent
// namespace extension methods
return null;
var cand = candidates;
- arguments.Insert (0, new Argument (ExtensionExpression, Argument.AType.ExtensionType));
+ var atype = ConditionalAccess ? Argument.AType.ExtensionTypeConditionalAccess : Argument.AType.ExtensionType;
+ arguments.Insert (0, new Argument (ExtensionExpression, atype));
var res = base.OverloadResolve (ec, ref arguments, ehandler ?? this, restr);
// Restore candidates in case we are running in probing mode
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 (ConditionalAccess)
+ 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.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)
// TODO: When in probing mode do IsApplicable only and when called again do VerifyArguments for full error reporting
best_candidate = r.ResolveMember<MethodSpec> (ec, ref args);
- if (best_candidate == null)
- return r.BestCandidateIsDynamic ? this : null;
+ if (best_candidate == null) {
+ if (!r.BestCandidateIsDynamic)
+ return null;
+
+ if (simple_name != null && ec.IsStatic)
+ InstanceExpression = ProbeIdenticalTypeName (ec, InstanceExpression, simple_name);
+
+ return this;
+ }
// Overload resolver had to create a new method group, all checks bellow have already been executed
if (r.BestCandidateNewMethodGroup != null)
if (best_candidate.IsExtensionMethod && args[0].Expr == InstanceExpression) {
InstanceExpression = null;
} else {
- if (best_candidate.IsStatic && simple_name != null) {
+ if (simple_name != null && best_candidate.IsStatic) {
InstanceExpression = ProbeIdenticalTypeName (ec, InstanceExpression, simple_name);
}
ProbingOnly = 1 << 1,
CovariantDelegate = 1 << 2,
NoBaseMembers = 1 << 3,
- BaseMembersIncluded = 1 << 4
+ BaseMembersIncluded = 1 << 4,
+ GetEnumeratorLookup = 1 << 5
}
public interface IBaseMembersProvider
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);
}
// one being the virtual base for the parameter types and modifiers.
//
// A return value rates candidate method compatibility,
- // 0 = the best, int.MaxValue = the worst
// -1 = fatal error
+ // 0 = the best, int.MaxValue = the worst
//
- int IsApplicable (ResolveContext ec, ref Arguments arguments, int arg_count, ref MemberSpec candidate, IParametersMember pm, ref bool params_expanded_form, ref bool dynamicArgument, ref TypeSpec returnType)
+ int IsApplicable (ResolveContext ec, ref Arguments arguments, int arg_count, ref MemberSpec candidate, IParametersMember pm, ref bool params_expanded_form, ref bool dynamicArgument, ref TypeSpec returnType, bool errorMode)
{
+ //
+ // Each step has allocated 10 values, it can overflow for
+ // more than 10 arguments but that's ok as it's used for
+ // better error reporting only
+ //
+ const int ArgumentCountMismatch = 1000000000;
+ const int NamedArgumentsMismatch = 100000000;
+ const int DefaultArgumentMismatch = 10000000;
+ const int UnexpectedTypeArguments = 1000000;
+ const int TypeArgumentsMismatch = 100000;
+ const int InflatedTypesMismatch = 10000;
+
// Parameters of most-derived type used mainly for named and optional parameters
var pd = pm.Parameters;
param_count--;
} else if (arg_count > param_count) {
int args_gap = System.Math.Abs (arg_count - param_count);
- return int.MaxValue - 10000 + args_gap;
+ return ArgumentCountMismatch + args_gap;
} else if (arg_count < param_count - optional_count) {
int args_gap = System.Math.Abs (param_count - optional_count - arg_count);
- return int.MaxValue - 10000 + args_gap;
+ return ArgumentCountMismatch + args_gap;
}
} else if (arg_count != param_count) {
int args_gap = System.Math.Abs (arg_count - param_count);
if (!cpd.HasParams)
- return int.MaxValue - 10000 + args_gap;
+ return ArgumentCountMismatch + args_gap;
if (arg_count < param_count - 1)
- return int.MaxValue - 10000 + args_gap;
+ return ArgumentCountMismatch + args_gap;
}
// Resize to fit optional arguments
// Named parameter not found
if (index < 0)
- return (i + 1) * 3;
+ return NamedArgumentsMismatch - i;
// already reordered
if (index == i)
++arg_count;
temp = null;
} else {
- if (index == arg_count)
- return (i + 1) * 3;
+ if (index == arg_count)
+ return NamedArgumentsMismatch - i - 1;
temp = arguments [index];
// Don't do any expensive checks when the candidate cannot succeed
//
if (arg_count != param_count && !cpd.HasParams)
- return (param_count - arg_count) * 2 + 1;
+ return DefaultArgumentMismatch - System.Math.Abs (param_count - arg_count);
var dep = candidate.GetMissingDependencies ();
if (dep != null) {
if (type_arguments != null) {
var g_args_count = ms.Arity;
if (g_args_count != type_arguments.Count)
- return int.MaxValue - 20000 + System.Math.Abs (type_arguments.Count - g_args_count);
+ return TypeArgumentsMismatch - System.Math.Abs (type_arguments.Count - g_args_count);
if (type_arguments.Arguments != null)
ms = ms.MakeGenericMethod (ec, type_arguments.Arguments);
TypeSpec[] i_args = ti.InferMethodArguments (ec, ms);
if (i_args == null)
- return ti.InferenceScore - 20000;
+ return TypeArgumentsMismatch - ti.InferenceScore;
//
// Clear any error messages when the result was success
lambda_conv_msgs.ClearSession ();
if (i_args.Length != 0) {
+ if (!errorMode) {
+ for (int i = 0; i < i_args.Length; ++i) {
+ var ta = i_args [i];
+ if (!ta.IsAccessible (ec))
+ return TypeArgumentsMismatch - i;
+ }
+ }
+
ms = ms.MakeGenericMethod (ec, i_args);
}
}
//
if (!CheckInflatedArguments (ms)) {
candidate = ms;
- return int.MaxValue - 25000;
+ return InflatedTypesMismatch;
}
//
ptypes = pd.Types;
} else {
if (type_arguments != null)
- return int.MaxValue - 15000;
+ return UnexpectedTypeArguments;
ptypes = cpd.Types;
}
score = 1;
if (!params_expanded_form) {
- if (a.ArgType == Argument.AType.ExtensionType) {
+ if (a.IsExtensionType) {
//
// Indentity, implicit reference or boxing conversion must exist for the extension parameter
//
// is used and argument is not of dynamic type
//
if (((argument.Modifier | param_mod) & Parameter.Modifier.RefOutMask) != 0) {
- if (argument.Type != parameter) {
+ var arg_type = argument.Type;
+
+ if ((argument.Modifier & Parameter.Modifier.RefOutMask) != (param_mod & Parameter.Modifier.RefOutMask)) {
+ //
+ // Using dynamic for ref/out parameter can still succeed at runtime
+ //
+ if (arg_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (argument.Modifier & Parameter.Modifier.RefOutMask) == 0 && (restrictions & Restrictions.CovariantDelegate) == 0)
+ return -1;
+
+ return 1;
+ }
+
+ if (arg_type != parameter) {
+ if (arg_type == InternalType.VarOutType)
+ return 0;
+
//
// Do full equality check after quick path
//
- if (!TypeSpecComparer.IsEqual (argument.Type, parameter)) {
+ if (!TypeSpecComparer.IsEqual (arg_type, parameter)) {
//
// Using dynamic for ref/out parameter can still succeed at runtime
//
- if (argument.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (argument.Modifier & Parameter.Modifier.RefOutMask) == 0 && (restrictions & Restrictions.CovariantDelegate) == 0)
+ if (arg_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (argument.Modifier & Parameter.Modifier.RefOutMask) == 0 && (restrictions & Restrictions.CovariantDelegate) == 0)
return -1;
return 2;
}
}
- if ((argument.Modifier & Parameter.Modifier.RefOutMask) != (param_mod & Parameter.Modifier.RefOutMask)) {
- //
- // Using dynamic for ref/out parameter can still succeed at runtime
- //
- if (argument.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (argument.Modifier & Parameter.Modifier.RefOutMask) == 0 && (restrictions & Restrictions.CovariantDelegate) == 0)
- return -1;
-
- return 1;
- }
-
} else {
if (argument.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (restrictions & Restrictions.CovariantDelegate) == 0)
return -1;
bool params_expanded_form = false;
bool dynamic_argument = false;
TypeSpec rt = pm.MemberType;
- int candidate_rate = IsApplicable (rc, ref candidate_args, args_count, ref member, pm, ref params_expanded_form, ref dynamic_argument, ref rt);
+ int candidate_rate = IsApplicable (rc, ref candidate_args, args_count, ref member, pm, ref params_expanded_form, ref dynamic_argument, ref rt, error_mode);
if (lambda_conv_msgs != null)
lambda_conv_msgs.EndSession ();
if (candidate_rate < 0)
return null;
- best_candidate_rate = candidate_rate;
- best_candidate = member;
- best_candidate_args = candidate_args;
- best_candidate_params = params_expanded_form;
- best_candidate_dynamic = dynamic_argument;
- best_parameter_member = pm;
- best_candidate_return_type = rt;
+ if ((restrictions & Restrictions.GetEnumeratorLookup) != 0 && candidate_args.Count != 0) {
+ // Only parameterless methods are considered
+ } else {
+ best_candidate_rate = candidate_rate;
+ best_candidate = member;
+ best_candidate_args = candidate_args;
+ best_candidate_params = params_expanded_form;
+ best_candidate_dynamic = dynamic_argument;
+ best_parameter_member = pm;
+ best_candidate_return_type = rt;
+ }
} else if (candidate_rate == 0) {
//
// The member look is done per type for most operations but sometimes
}
if (best_candidate_dynamic) {
- if (args[0].ArgType == Argument.AType.ExtensionType) {
+ if (args[0].IsExtensionType) {
rc.Report.Error (1973, loc,
"Type `{0}' does not contain a member `{1}' and the best extension method overload `{2}' cannot be dynamically dispatched. Consider calling the method without the extension method syntax",
args [0].Type.GetSignatureForError (), best_candidate.Name, best_candidate.GetSignatureForError ());
break;
}
+ if (args == null)
+ return false;
+
foreach (var arg in args) {
var na = arg as NamedArgument;
if (na == null)
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;
if ((a.Modifier & Parameter.Modifier.RefOutMask) != (p_mod & Parameter.Modifier.RefOutMask))
break;
- if (a.Expr.Type == pt || TypeSpecComparer.IsEqual (a.Expr.Type, pt))
+ var arg_type = a.Type;
+ if (arg_type == pt)
continue;
- break;
+ if (arg_type == InternalType.VarOutType) {
+ //
+ // Set underlying variable type based on parameter type
+ //
+ ((DeclarationExpression)a.Expr).Variable.Type = pt;
+ continue;
+ }
+
+ if (!TypeSpecComparer.IsEqual (arg_type, pt))
+ break;
}
NamedArgument na = a as NamedArgument;
}
Expression conv;
- if (a.ArgType == Argument.AType.ExtensionType) {
+ if (a.IsExtensionType) {
if (a.Expr.Type == pt || TypeSpecComparer.IsEqual (a.Expr.Type, pt)) {
conv = a.Expr;
} else {
}
if (a_idx != arg_count) {
+ //
+ // Convert all var out argument to error type for less confusing error reporting
+ // when no matching overload is found
+ //
+ for (; a_idx < arg_count; a_idx++) {
+ var arg = args [a_idx];
+ if (arg == null)
+ continue;
+
+ if (arg.Type == InternalType.VarOutType) {
+ ((DeclarationExpression)arg.Expr).Variable.Type = InternalType.ErrorType;
+ }
+ }
+
ReportArgumentMismatch (ec, a_pos, member, a, pd, pt);
return false;
}
//
// Fill not provided arguments required by params modifier
//
- if (params_initializers == null && pd.HasParams && arg_count + 1 == pd.Count) {
+ if (params_initializers == null && arg_count + 1 == pd.Count) {
if (args == null)
args = new Arguments (1);
public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (ConditionalAccess) {
+ Error_NullShortCircuitInsideExpressionTree (ec);
+ }
+
return CreateExpressionTree (ec, true);
}
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 (ConditionalAccess) {
+ if (conditional_access_receiver)
+ 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 (ConditionalAccess)
+ 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);
return;
}
- if (TypeSpec.IsValueType (InstanceExpression.Type))
+ if (TypeSpec.IsValueType (InstanceExpression.Type) && InstanceExpression is VariableReference)
return;
}
base.FlowAnalysis (fc);
+
+ if (conditional_access_receiver)
+ fc.ConditionalAccessEnd ();
}
public override int GetHashCode ()
ec.Emit (OpCodes.Ldsfld, spec);
} else {
- if (!prepared)
+ 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.Ldfld, spec);
}
}
+
+ if (conditional_access_receiver) {
+ ec.CloseConditionalAccess (type.IsNullableType && type != spec.MemberType ? type : null);
+ }
}
if (leave_copy) {
public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
{
bool has_await_source = ec.HasSet (BuilderContext.Options.AsyncBody) && source.ContainsEmitWithAwait ();
- if (isCompound && !(source is DynamicExpressionStatement)) {
- if (has_await_source) {
- if (IsInstance)
- InstanceExpression = InstanceExpression.EmitToField (ec);
- } else {
- prepared = true;
- }
+ if (isCompound && !(source is DynamicExpressionStatement) && !has_await_source) {
+ prepared = true;
}
if (IsInstance) {
+ if (ConditionalAccess)
+ throw new NotImplementedException ("null operator assignment");
+
if (has_await_source)
source = source.EmitToField (ec);
source.Emit (ec);
- if (leave_copy) {
+ if (leave_copy || ec.NotifyEvaluatorOnStore) {
ec.Emit (OpCodes.Dup);
if (!IsStatic) {
temp = new LocalTemporary (this.Type);
ec.Emit (OpCodes.Stsfld, spec);
else
ec.Emit (OpCodes.Stfld, spec);
+
+ if (ec.NotifyEvaluatorOnStore) {
+ if (!IsStatic)
+ throw new NotImplementedException ("instance field write");
+
+ if (leave_copy)
+ ec.Emit (OpCodes.Dup);
+
+ ec.Module.Evaluator.EmitValueChangedCallback (ec, Name, type, loc);
+ }
if (temp != null) {
temp.Emit (ec);
public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (ConditionalAccess) {
+ 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 ()) {
+ if (conditional_access_receiver) {
+ ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+ }
+
EmitInstance (ec, false);
+
ec.Emit (OpCodes.Ldlen);
ec.Emit (OpCodes.Conv_I4);
+
+ if (conditional_access_receiver) {
+ ec.CloseConditionalAccess (type);
+ }
+
return;
}
if (args == null)
call.InstanceExpressionOnStack = true;
- call.Emit (ec, Setter, args, loc);
+ if (ConditionalAccess) {
+ call.ConditionalAccess = true;
+ }
+
+ if (leave_copy)
+ call.Emit (ec, Setter, args, loc);
+ else
+ call.EmitStatement (ec, Setter, args, loc);
if (temp != null) {
temp.Emit (ec);
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ base.FlowAnalysis (fc);
+
+ if (conditional_access_receiver)
+ fc.ConditionalAccessEnd ();
+ }
+
protected override Expression OverloadResolve (ResolveContext rc, Expression right_side)
{
eclass = ExprClass.PropertyAccess;
protected override Expression DoResolve (ResolveContext ec)
{
if (eclass == ExprClass.Unresolved) {
+ ResolveConditionalAccessReceiver (ec);
+
var expr = OverloadResolve (ec, null);
if (expr == null)
return null;
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 (ConditionalAccess)
+ throw new NotSupportedException ("null propagating operator assignment");
+
if (right_side == EmptyExpression.OutAccess) {
// TODO: best_candidate can be null at this point
INamedBlockVariable variable = null;
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.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;
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.Emit (ec, op, args, loc);
+ call.ConditionalAccess = ConditionalAccess;
+ call.EmitStatement (ec, op, args, loc);
+
+// if (NullShortCircuit)
+// ec.CloseConditionalAccess (null);
}
#endregion