{
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.
return e;
} catch (Exception ex) {
- if (loc.IsNull || ec.Module.Compiler.Settings.DebugFlags > 0 || ex is CompletionResult || ec.Report.IsDisabled || ex is FatalException ||
+ if (loc.IsNull || ec.Module.Compiler.Settings.BreakOnInternalError || ex is CompletionResult || ec.Report.IsDisabled || ex is FatalException ||
ec.Report.Printer is NullReportPrinter)
throw;
return c;
}
- public virtual void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType)
+ public virtual void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType)
{
- rc.Module.Compiler.Report.Error (182, loc,
- "An attribute argument must be a constant expression, typeof expression or array creation expression");
+ if (Attribute.IsValidArgumentType (parameterType)) {
+ rc.Module.Compiler.Report.Error (182, loc,
+ "An attribute argument must be a constant expression, typeof expression or array creation expression");
+ } else {
+ rc.Module.Compiler.Report.Error (181, loc,
+ "Attribute constructor parameter has type `{0}', which is not a valid attribute parameter type",
+ targetType.GetSignatureForError ());
+ }
}
/// <summary>
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)
{
}
Child.Emit (ec);
}
- public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType)
+ public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType)
{
- Child.EncodeAttributeValue (rc, enc, Child.Type);
+ Child.EncodeAttributeValue (rc, enc, Child.Type, parameterType);
}
public override void EmitBranchable (EmitContext ec, Label label, bool on_true)
return this;
}
- public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType)
+ public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType)
{
// Only boxing to object type is supported
if (targetType.BuiltinType != BuiltinTypeSpec.Type.Object) {
- base.EncodeAttributeValue (rc, enc, targetType);
+ base.EncodeAttributeValue (rc, enc, targetType, parameterType);
return;
}
enc.Encode (child.Type);
- child.EncodeAttributeValue (rc, enc, child.Type);
+ child.EncodeAttributeValue (rc, enc, child.Type, parameterType);
}
public override void Emit (EmitContext ec)
return c;
}
- public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType)
+ public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType)
{
//
// LAMESPEC: Reduced conditional expression is allowed as an attribute argument
//
if (orig_expr is Conditional)
- child.EncodeAttributeValue (rc, enc, targetType);
+ child.EncodeAttributeValue (rc, enc, targetType,parameterType);
else
- base.EncodeAttributeValue (rc, enc, targetType);
+ base.EncodeAttributeValue (rc, enc, targetType, parameterType);
}
}
//
// dynamic namespace is ignored when dynamic is allowed (does not apply to types)
//
- if (!(fne is Namespace))
+ if (!(fne is NamespaceExpression))
return fne;
}
}
}
+ var mg = NamespaceContainer.LookupStaticUsings (rc, Name, Arity, loc);
+ if (mg != null) {
+ if (Arity > 0) {
+ targs.Resolve (rc);
+ mg.SetTypeArguments (rc, targs);
+ }
+ return mg;
+ }
+
if (errorMode) {
if (variable_found) {
rc.Report.Error (841, loc, "A local variable `{0}' cannot be used before it is declared", Name);
}
}
+ public class NamespaceExpression : FullNamedExpression
+ {
+ readonly Namespace ns;
+
+ public NamespaceExpression (Namespace ns, Location loc)
+ {
+ this.ns = ns;
+ this.Type = InternalType.Namespace;
+ this.eclass = ExprClass.Namespace;
+ this.loc = loc;
+ }
+
+ public Namespace Namespace {
+ get {
+ return ns;
+ }
+ }
+
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc)
+ {
+ return this;
+ }
+
+ public void Error_NamespaceDoesNotExist (IMemberContext ctx, string name, int arity)
+ {
+ var retval = Namespace.LookupType (ctx, name, arity, LookupMode.IgnoreAccessibility, loc);
+ if (retval != null) {
+// ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (retval.MemberDefinition);
+ ErrorIsInaccesible (ctx, retval.GetSignatureForError (), loc);
+ return;
+ }
+
+ retval = Namespace.LookupType (ctx, name, -System.Math.Max (1, arity), LookupMode.Probing, loc);
+ if (retval != null) {
+ Error_TypeArgumentsCannotBeUsed (ctx, retval, loc);
+ return;
+ }
+
+ Namespace ns;
+ if (arity > 0 && Namespace.TryGetNamespace (name, out ns)) {
+ Error_TypeArgumentsCannotBeUsed (ctx, ExprClassName, ns.GetSignatureForError (), loc);
+ return;
+ }
+
+ string assembly = null;
+ string possible_name = Namespace.GetSignatureForError () + "." + name;
+
+ // Only assembly unique name should be added
+ switch (possible_name) {
+ case "System.Drawing":
+ case "System.Web.Services":
+ case "System.Web":
+ case "System.Data":
+ case "System.Configuration":
+ case "System.Data.Services":
+ case "System.DirectoryServices":
+ case "System.Json":
+ case "System.Net.Http":
+ case "System.Numerics":
+ case "System.Runtime.Caching":
+ case "System.ServiceModel":
+ case "System.Transactions":
+ case "System.Web.Routing":
+ case "System.Xml.Linq":
+ case "System.Xml":
+ assembly = possible_name;
+ break;
+
+ case "System.Linq":
+ case "System.Linq.Expressions":
+ assembly = "System.Core";
+ break;
+
+ case "System.Windows.Forms":
+ case "System.Windows.Forms.Layout":
+ assembly = "System.Windows.Forms";
+ break;
+ }
+
+ assembly = assembly == null ? "an" : "`" + assembly + "'";
+
+ if (Namespace is GlobalRootNamespace) {
+ ctx.Module.Compiler.Report.Error (400, loc,
+ "The type or namespace name `{0}' could not be found in the global namespace. Are you missing {1} assembly reference?",
+ name, assembly);
+ } else {
+ ctx.Module.Compiler.Report.Error (234, loc,
+ "The type or namespace name `{0}' does not exist in the namespace `{1}'. Are you missing {2} assembly reference?",
+ name, GetSignatureForError (), assembly);
+ }
+ }
+
+ public override string GetSignatureForError ()
+ {
+ return ns.GetSignatureForError ();
+ }
+
+ public FullNamedExpression LookupTypeOrNamespace (IMemberContext ctx, string name, int arity, LookupMode mode, Location loc)
+ {
+ return ns.LookupTypeOrNamespace (ctx, name, arity, mode, loc);
+ }
+ }
+
/// <summary>
/// This class denotes an expression which evaluates to a member
/// of a struct or a class.
get;
}
+ public bool NullShortCircuit { get; set; }
+
protected abstract TypeSpec DeclaringType {
get;
}
//
// 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))
if (InstanceExpression == null || InstanceExpression is TypeExpr) {
if (InstanceExpression != null || !This.IsThisAvailable (rc, true)) {
- if (rc.HasSet (ResolveContext.Options.FieldInitializerScope))
+ if (rc.HasSet (ResolveContext.Options.FieldInitializerScope)) {
rc.Report.Error (236, loc,
"A field initializer cannot reference the nonstatic field, method, or property `{0}'",
GetSignatureForError ());
- else
- rc.Report.Error (120, loc,
- "An object reference is required to access non-static member `{0}'",
- GetSignatureForError ());
+ } else {
+ var fe = this as FieldExpr;
+ if (fe != null && fe.Spec.MemberDefinition is PrimaryConstructorField) {
+ if (rc.HasSet (ResolveContext.Options.BaseInitializer)) {
+ rc.Report.Error (9005, loc, "Constructor initializer cannot access primary constructor parameters");
+ } else {
+ rc.Report.Error (9006, loc, "An object reference is required to access primary constructor parameter `{0}'",
+ fe.Name);
+ }
+ } else {
+ rc.Report.Error (120, loc,
+ "An object reference is required to access non-static member `{0}'",
+ GetSignatureForError ());
+ }
+ }
InstanceExpression = new CompilerGeneratedThis (rc.CurrentType, loc).Resolve (rc);
return false;
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)
// 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);
}
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;
}
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);
}
AParametersCollection best_pd = ((IParametersMember) best).Parameters;
bool better_at_least_one = false;
- bool same = true;
+ bool are_equivalent = true;
int args_count = args == null ? 0 : args.Count;
int j = 0;
Argument a = null;
if (TypeSpecComparer.IsEqual (ct, bt))
continue;
- same = false;
+ are_equivalent = false;
int result = BetterExpressionConversion (ec, a, ct, bt);
// for each argument, the conversion to 'ct' should be no worse than
return true;
//
- // This handles the case
- //
- // Add (float f1, float f2, float f3);
- // Add (params decimal [] foo);
+ // Tie-breaking rules are applied only for equivalent parameter types
//
- // The call Add (3, 4, 5) should be ambiguous. Without this check, the
- // first candidate would've chosen as better.
- //
- if (!same && !a.IsDefaultArgument)
+ if (!are_equivalent)
return false;
//
- // The two methods have equal non-optional parameter types, apply tie-breaking rules
+ // If candidate is applicable in its normal form and best has a params array and is applicable
+ // only in its expanded form, then candidate is better
//
+ if (candidate_params != best_params)
+ return !candidate_params;
//
- // This handles the following cases:
- //
- // Foo (int i) is better than Foo (int i, long l = 0)
- // Foo (params int[] args) is better than Foo (int i = 0, params int[] args)
- // Foo (string s, params string[] args) is better than Foo (params string[] args)
- //
- // Prefer non-optional version
- //
- // LAMESPEC: Specification claims this should be done at last but the opposite is true
+ // We have not reached end of parameters list due to params or used default parameters
//
- if (candidate_params == best_params && candidate_pd.Count != best_pd.Count) {
- if (j < candidate_pd.Count && candidate_pd.FixedParameters[j].HasDefaultValue)
- return false;
+ while (j < candidate_pd.Count && j < best_pd.Count) {
+ var cand_param = candidate_pd.FixedParameters [j];
+ var best_param = best_pd.FixedParameters [j];
- if (j < best_pd.Count && best_pd.FixedParameters[j].HasDefaultValue)
- return true;
+ if (candidate_pd.Count == best_pd.Count) {
+ //
+ // LAMESPEC:
+ //
+ // void Foo (int i = 0) is better than void Foo (params int[]) for Foo ()
+ // void Foo (string[] s, string value = null) is better than Foo (string s, params string[]) for Foo (null) or Foo ()
+ //
+ if (cand_param.HasDefaultValue != best_param.HasDefaultValue)
+ return cand_param.HasDefaultValue;
- return candidate_pd.Count >= best_pd.Count;
+ if (cand_param.HasDefaultValue) {
+ ++j;
+ continue;
+ }
+ } else {
+ //
+ // Neither is better when not all arguments are provided
+ //
+ // void Foo (string s, int i = 0) <-> Foo (string s, int i = 0, int i2 = 0)
+ // void Foo (string s, int i = 0) <-> Foo (string s, byte i = 0)
+ // void Foo (string s, params int[]) <-> Foo (string s, params byte[])
+ //
+ if (cand_param.HasDefaultValue && best_param.HasDefaultValue)
+ return false;
+ }
+
+ break;
}
+ if (candidate_pd.Count != best_pd.Count)
+ return candidate_pd.Count < best_pd.Count;
+
//
// One is a non-generic method and second is a generic method, then non-generic is better
//
if (best.IsGeneric != candidate.IsGeneric)
return best.IsGeneric;
- //
- // This handles the following cases:
- //
- // Trim () is better than Trim (params char[] chars)
- // Concat (string s1, string s2, string s3) is better than
- // Concat (string s1, params string [] srest)
- // Foo (int, params int [] rest) is better than Foo (params int [] rest)
- //
- // Prefer non-expanded version
- //
- if (candidate_params != best_params)
- return best_params;
-
- int candidate_param_count = candidate_pd.Count;
- int best_param_count = best_pd.Count;
-
- if (candidate_param_count != best_param_count)
- // can only happen if (candidate_params && best_params)
- return candidate_param_count > best_param_count && best_pd.HasParams;
-
//
// Both methods have the same number of parameters, and the parameters have equal types
// Pick the "more specific" signature using rules over original (non-inflated) types
// 0 = the best, int.MaxValue = the worst
// -1 = fatal error
//
- 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)
{
// Parameters of most-derived type used mainly for named and optional parameters
var pd = pm.Parameters;
++arg_count;
temp = null;
} else {
- temp = arguments[index];
+ if (index == arg_count)
+ return (i + 1) * 3;
+
+ temp = arguments [index];
// The slot has been taken by positional argument
if (temp != null && !(temp is NamedArgument))
if (g_args_count != type_arguments.Count)
return int.MaxValue - 20000 + System.Math.Abs (type_arguments.Count - g_args_count);
- ms = ms.MakeGenericMethod (ec, type_arguments.Arguments);
+ if (type_arguments.Arguments != null)
+ ms = ms.MakeGenericMethod (ec, type_arguments.Arguments);
} else {
//
// Deploy custom error reporting for infered anonymous expression or lambda methods. When
lambda_conv_msgs.ClearSession ();
if (i_args.Length != 0) {
+ if (!errorMode) {
+ foreach (var ta in i_args) {
+ if (!ta.IsAccessible (ec))
+ return ti.InferenceScore - 10000;
+ }
+ }
+
ms = ms.MakeGenericMethod (ec, i_args);
}
}
}
}
- //
- // When params parameter has no argument it will be provided later if the method is the best candidate
- //
- if (arg_count + 1 == pd.Count && (cpd.FixedParameters [arg_count].ModFlags & Parameter.Modifier.PARAMS) != 0)
- params_expanded_form = true;
-
//
// Restore original arguments for dynamic binder to keep the intention of original source code
//
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
// For candidates which match on parameters count report more details about incorrect arguments
//
if (pm != null) {
- int unexpanded_count = ((IParametersMember) best_candidate).Parameters.HasParams ? pm.Parameters.Count - 1 : pm.Parameters.Count;
- if (pm.Parameters.Count == arg_count || params_expanded || unexpanded_count == arg_count) {
+ if (pm.Parameters.Count == arg_count || params_expanded || HasUnfilledParams (best_candidate, pm, args)) {
// Reject any inaccessible member
if (!best_candidate.IsAccessible (rc) || !best_candidate.DeclaringType.IsAccessible (rc)) {
rc.Report.SymbolRelatedToPreviousError (best_candidate);
}
}
+ static bool HasUnfilledParams (MemberSpec best_candidate, IParametersMember pm, Arguments args)
+ {
+ var p = ((IParametersMember)best_candidate).Parameters;
+ if (!p.HasParams)
+ return false;
+
+ string name = null;
+ for (int i = p.Count - 1; i != 0; --i) {
+ var fp = p.FixedParameters [i];
+ if ((fp.ModFlags & Parameter.Modifier.PARAMS) == 0)
+ continue;
+
+ name = fp.Name;
+ break;
+ }
+
+ if (args == null)
+ return false;
+
+ foreach (var arg in args) {
+ var na = arg as NamedArgument;
+ if (na == null)
+ continue;
+
+ if (na.Name == name) {
+ name = null;
+ break;
+ }
+ }
+
+ if (name == null)
+ return false;
+
+ return args.Count + 1 == pm.Parameters.Count;
+ }
+
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;
//
// 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);
}
}
+ protected override void CloneTo (CloneContext clonectx, Expression target)
+ {
+ var t = (FieldExpr) target;
+
+ if (InstanceExpression != null)
+ t.InstanceExpression = InstanceExpression.Clone (clonectx);
+ }
+
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);
return;
}
- if (TypeSpec.IsValueType (InstanceExpression.Type))
+ if (TypeSpec.IsValueType (InstanceExpression.Type) && InstanceExpression is VariableReference)
return;
}
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);
+ }
}
}
}
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 (NullShortCircuit)
+ 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);
var temp = ec.GetTemporaryLocal (type);
ec.Emit (OpCodes.Stloc, temp);
ec.Emit (OpCodes.Ldloca, temp);
- ec.FreeTemporaryLocal (temp, type);
return;
}
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