readonly struct this can be reassigned within ctor
[mono-project.git] / mcs / mcs / expression.cs
blob183be4d8cb3b3eb60387409c8f8756bf63b30c41
1 //
2 // expression.cs: Expression representation for the IL tree.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
7 //
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2003-2008 Novell, Inc.
10 // Copyright 2011 Xamarin Inc.
13 using System;
14 using System.Collections.Generic;
15 using System.Linq;
16 using SLE = System.Linq.Expressions;
17 using System.Text;
19 #if STATIC
20 using MetaType = IKVM.Reflection.Type;
21 using IKVM.Reflection;
22 using IKVM.Reflection.Emit;
23 #else
24 using MetaType = System.Type;
25 using System.Reflection;
26 using System.Reflection.Emit;
27 #endif
29 namespace Mono.CSharp
32 // This is an user operator expression, automatically created during
33 // resolve phase
35 public class UserOperatorCall : Expression {
36 protected readonly Arguments arguments;
37 protected readonly MethodSpec oper;
38 readonly Func<ResolveContext, Expression, Expression> expr_tree;
40 public UserOperatorCall (MethodSpec oper, Arguments args, Func<ResolveContext, Expression, Expression> expr_tree, Location loc)
42 this.oper = oper;
43 this.arguments = args;
44 this.expr_tree = expr_tree;
46 type = oper.ReturnType;
47 eclass = ExprClass.Value;
48 this.loc = loc;
51 public override bool ContainsEmitWithAwait ()
53 return arguments.ContainsEmitWithAwait ();
56 public override Expression CreateExpressionTree (ResolveContext ec)
58 if (expr_tree != null)
59 return expr_tree (ec, new TypeOfMethod (oper, loc));
61 Arguments args = Arguments.CreateForExpressionTree (ec, arguments,
62 new NullLiteral (loc),
63 new TypeOfMethod (oper, loc));
65 return CreateExpressionFactoryCall (ec, "Call", args);
68 protected override void CloneTo (CloneContext context, Expression target)
70 // Nothing to clone
73 protected override Expression DoResolve (ResolveContext ec)
76 // We are born fully resolved
78 return this;
81 public override void Emit (EmitContext ec)
83 var call = new CallEmitter ();
84 call.Emit (ec, oper, arguments, loc);
87 public override void FlowAnalysis (FlowAnalysisContext fc)
89 arguments.FlowAnalysis (fc);
92 public override SLE.Expression MakeExpression (BuilderContext ctx)
94 #if STATIC
95 return base.MakeExpression (ctx);
96 #else
97 return SLE.Expression.Call ((MethodInfo) oper.GetMetaInfo (), Arguments.MakeExpression (arguments, ctx));
98 #endif
102 public class ParenthesizedExpression : ShimExpression
104 public ParenthesizedExpression (Expression expr, Location loc)
105 : base (expr)
107 this.loc = loc;
110 protected override Expression DoResolve (ResolveContext rc)
112 Expression res = null;
113 using (rc.With (ResolveContext.Options.DontSetConditionalAccessReceiver, false)) {
114 res = expr.Resolve (rc);
117 var constant = res as Constant;
118 if (constant != null && constant.IsLiteral) {
119 if (res is NullLiteral)
120 return res;
122 return Constant.CreateConstantFromValue (res.Type, constant.GetValue (), expr.Location);
125 return res;
128 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
130 return expr.DoResolveLValue (ec, right_side);
133 public override object Accept (StructuralVisitor visitor)
135 return visitor.Visit (this);
138 public override bool HasConditionalAccess ()
140 return false;
145 // Unary implements unary expressions.
147 public class Unary : Expression
149 public enum Operator : byte {
150 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
151 AddressOf, TOP
154 public readonly Operator Oper;
155 public Expression Expr;
156 ConvCast.Mode enum_conversion;
158 public Unary (Operator op, Expression expr, Location loc)
160 Oper = op;
161 Expr = expr;
162 this.loc = loc;
165 // <summary>
166 // This routine will attempt to simplify the unary expression when the
167 // argument is a constant.
168 // </summary>
169 Constant TryReduceConstant (ResolveContext ec, Constant constant)
171 var e = constant;
173 while (e is EmptyConstantCast)
174 e = ((EmptyConstantCast) e).child;
176 if (e is SideEffectConstant) {
177 Constant r = TryReduceConstant (ec, ((SideEffectConstant) e).value);
178 return r == null ? null : new SideEffectConstant (r, e, r.Location);
181 TypeSpec expr_type = e.Type;
183 switch (Oper){
184 case Operator.UnaryPlus:
185 // Unary numeric promotions
186 switch (expr_type.BuiltinType) {
187 case BuiltinTypeSpec.Type.Byte:
188 return new IntConstant (ec.BuiltinTypes, ((ByteConstant) e).Value, e.Location);
189 case BuiltinTypeSpec.Type.SByte:
190 return new IntConstant (ec.BuiltinTypes, ((SByteConstant) e).Value, e.Location);
191 case BuiltinTypeSpec.Type.Short:
192 return new IntConstant (ec.BuiltinTypes, ((ShortConstant) e).Value, e.Location);
193 case BuiltinTypeSpec.Type.UShort:
194 return new IntConstant (ec.BuiltinTypes, ((UShortConstant) e).Value, e.Location);
195 case BuiltinTypeSpec.Type.Char:
196 return new IntConstant (ec.BuiltinTypes, ((CharConstant) e).Value, e.Location);
198 // Predefined operators
199 case BuiltinTypeSpec.Type.Int:
200 case BuiltinTypeSpec.Type.UInt:
201 case BuiltinTypeSpec.Type.Long:
202 case BuiltinTypeSpec.Type.ULong:
203 case BuiltinTypeSpec.Type.Float:
204 case BuiltinTypeSpec.Type.Double:
205 case BuiltinTypeSpec.Type.Decimal:
206 return e;
209 return null;
211 case Operator.UnaryNegation:
212 // Unary numeric promotions
213 switch (expr_type.BuiltinType) {
214 case BuiltinTypeSpec.Type.Byte:
215 return new IntConstant (ec.BuiltinTypes, -((ByteConstant) e).Value, e.Location);
216 case BuiltinTypeSpec.Type.SByte:
217 return new IntConstant (ec.BuiltinTypes, -((SByteConstant) e).Value, e.Location);
218 case BuiltinTypeSpec.Type.Short:
219 return new IntConstant (ec.BuiltinTypes, -((ShortConstant) e).Value, e.Location);
220 case BuiltinTypeSpec.Type.UShort:
221 return new IntConstant (ec.BuiltinTypes, -((UShortConstant) e).Value, e.Location);
222 case BuiltinTypeSpec.Type.Char:
223 return new IntConstant (ec.BuiltinTypes, -((CharConstant) e).Value, e.Location);
225 // Predefined operators
226 case BuiltinTypeSpec.Type.Int:
227 int ivalue = ((IntConstant) e).Value;
228 if (ivalue == int.MinValue) {
229 if (ec.ConstantCheckState) {
230 ConstantFold.Error_CompileTimeOverflow (ec, loc);
231 return null;
233 return e;
235 return new IntConstant (ec.BuiltinTypes, -ivalue, e.Location);
237 case BuiltinTypeSpec.Type.Long:
238 long lvalue = ((LongConstant) e).Value;
239 if (lvalue == long.MinValue) {
240 if (ec.ConstantCheckState) {
241 ConstantFold.Error_CompileTimeOverflow (ec, loc);
242 return null;
244 return e;
246 return new LongConstant (ec.BuiltinTypes, -lvalue, e.Location);
248 case BuiltinTypeSpec.Type.UInt:
249 UIntLiteral uil = constant as UIntLiteral;
250 if (uil != null) {
251 if (uil.Value == int.MaxValue + (uint) 1)
252 return new IntLiteral (ec.BuiltinTypes, int.MinValue, e.Location);
253 return new LongLiteral (ec.BuiltinTypes, -uil.Value, e.Location);
255 return new LongConstant (ec.BuiltinTypes, -((UIntConstant) e).Value, e.Location);
258 case BuiltinTypeSpec.Type.ULong:
259 ULongLiteral ull = constant as ULongLiteral;
260 if (ull != null && ull.Value == 9223372036854775808)
261 return new LongLiteral (ec.BuiltinTypes, long.MinValue, e.Location);
262 return null;
264 case BuiltinTypeSpec.Type.Float:
265 FloatLiteral fl = constant as FloatLiteral;
266 // For better error reporting
267 if (fl != null)
268 return new FloatLiteral (ec.BuiltinTypes, -fl.Value, e.Location);
270 return new FloatConstant (ec.BuiltinTypes, -((FloatConstant) e).Value, e.Location);
272 case BuiltinTypeSpec.Type.Double:
273 DoubleLiteral dl = constant as DoubleLiteral;
274 // For better error reporting
275 if (dl != null)
276 return new DoubleLiteral (ec.BuiltinTypes, -dl.Value, e.Location);
278 return new DoubleConstant (ec.BuiltinTypes, -((DoubleConstant) e).Value, e.Location);
280 case BuiltinTypeSpec.Type.Decimal:
281 return new DecimalConstant (ec.BuiltinTypes, -((DecimalConstant) e).Value, e.Location);
284 return null;
286 case Operator.LogicalNot:
287 if (expr_type.BuiltinType != BuiltinTypeSpec.Type.Bool)
288 return null;
290 bool b = (bool)e.GetValue ();
291 return new BoolConstant (ec.BuiltinTypes, !b, e.Location);
293 case Operator.OnesComplement:
294 // Unary numeric promotions
295 switch (expr_type.BuiltinType) {
296 case BuiltinTypeSpec.Type.Byte:
297 return new IntConstant (ec.BuiltinTypes, ~((ByteConstant) e).Value, e.Location);
298 case BuiltinTypeSpec.Type.SByte:
299 return new IntConstant (ec.BuiltinTypes, ~((SByteConstant) e).Value, e.Location);
300 case BuiltinTypeSpec.Type.Short:
301 return new IntConstant (ec.BuiltinTypes, ~((ShortConstant) e).Value, e.Location);
302 case BuiltinTypeSpec.Type.UShort:
303 return new IntConstant (ec.BuiltinTypes, ~((UShortConstant) e).Value, e.Location);
304 case BuiltinTypeSpec.Type.Char:
305 return new IntConstant (ec.BuiltinTypes, ~((CharConstant) e).Value, e.Location);
307 // Predefined operators
308 case BuiltinTypeSpec.Type.Int:
309 return new IntConstant (ec.BuiltinTypes, ~((IntConstant)e).Value, e.Location);
310 case BuiltinTypeSpec.Type.UInt:
311 return new UIntConstant (ec.BuiltinTypes, ~((UIntConstant) e).Value, e.Location);
312 case BuiltinTypeSpec.Type.Long:
313 return new LongConstant (ec.BuiltinTypes, ~((LongConstant) e).Value, e.Location);
314 case BuiltinTypeSpec.Type.ULong:
315 return new ULongConstant (ec.BuiltinTypes, ~((ULongConstant) e).Value, e.Location);
317 if (e is EnumConstant) {
318 var res = TryReduceConstant (ec, ((EnumConstant)e).Child);
319 if (res != null) {
321 // Numeric promotion upgraded types to int but for enum constant
322 // original underlying constant type is needed
324 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.Int) {
325 int v = ((IntConstant) res).Value;
326 switch (((EnumConstant) e).Child.Type.BuiltinType) {
327 case BuiltinTypeSpec.Type.UShort:
328 res = new UShortConstant (ec.BuiltinTypes, (ushort) v, e.Location);
329 break;
330 case BuiltinTypeSpec.Type.Short:
331 res = new ShortConstant (ec.BuiltinTypes, (short) v, e.Location);
332 break;
333 case BuiltinTypeSpec.Type.Byte:
334 res = new ByteConstant (ec.BuiltinTypes, (byte) v, e.Location);
335 break;
336 case BuiltinTypeSpec.Type.SByte:
337 res = new SByteConstant (ec.BuiltinTypes, (sbyte) v, e.Location);
338 break;
342 res = new EnumConstant (res, expr_type);
344 return res;
346 return null;
348 throw new Exception ("Can not constant fold: " + Oper.ToString());
351 protected virtual Expression ResolveOperator (ResolveContext ec, Expression expr)
353 eclass = ExprClass.Value;
355 TypeSpec expr_type = expr.Type;
356 Expression best_expr;
358 TypeSpec[] predefined = ec.BuiltinTypes.OperatorsUnary [(int) Oper];
361 // Primitive types first
363 if (BuiltinTypeSpec.IsPrimitiveType (expr_type)) {
364 best_expr = ResolvePrimitivePredefinedType (ec, expr, predefined);
365 if (best_expr == null)
366 return null;
368 type = best_expr.Type;
369 Expr = best_expr;
370 return this;
374 // E operator ~(E x);
376 if (Oper == Operator.OnesComplement && expr_type.IsEnum)
377 return ResolveEnumOperator (ec, expr, predefined);
379 return ResolveUserType (ec, expr, predefined);
382 protected virtual Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
384 TypeSpec underlying_type = EnumSpec.GetUnderlyingType (expr.Type);
385 Expression best_expr = ResolvePrimitivePredefinedType (ec, EmptyCast.Create (expr, underlying_type), predefined);
386 if (best_expr == null)
387 return null;
389 Expr = best_expr;
390 enum_conversion = Binary.GetEnumResultCast (underlying_type);
391 type = expr.Type;
392 return EmptyCast.Create (this, type);
395 public override bool ContainsEmitWithAwait ()
397 return Expr.ContainsEmitWithAwait ();
400 public override Expression CreateExpressionTree (ResolveContext ec)
402 return CreateExpressionTree (ec, null);
405 Expression CreateExpressionTree (ResolveContext ec, Expression user_op)
407 string method_name;
408 switch (Oper) {
409 case Operator.AddressOf:
410 Error_PointerInsideExpressionTree (ec);
411 return null;
412 case Operator.UnaryNegation:
413 if (ec.HasSet (ResolveContext.Options.CheckedScope) && user_op == null && !IsFloat (type))
414 method_name = "NegateChecked";
415 else
416 method_name = "Negate";
417 break;
418 case Operator.OnesComplement:
419 case Operator.LogicalNot:
420 method_name = "Not";
421 break;
422 case Operator.UnaryPlus:
423 method_name = "UnaryPlus";
424 break;
425 default:
426 throw new InternalErrorException ("Unknown unary operator " + Oper.ToString ());
429 Arguments args = new Arguments (2);
430 args.Add (new Argument (Expr.CreateExpressionTree (ec)));
431 if (user_op != null)
432 args.Add (new Argument (user_op));
434 return CreateExpressionFactoryCall (ec, method_name, args);
437 public static TypeSpec[][] CreatePredefinedOperatorsTable (BuiltinTypes types)
439 var predefined_operators = new TypeSpec[(int) Operator.TOP][];
442 // 7.6.1 Unary plus operator
444 predefined_operators [(int) Operator.UnaryPlus] = new TypeSpec [] {
445 types.Int, types.UInt,
446 types.Long, types.ULong,
447 types.Float, types.Double,
448 types.Decimal
452 // 7.6.2 Unary minus operator
454 predefined_operators [(int) Operator.UnaryNegation] = new TypeSpec [] {
455 types.Int, types.Long,
456 types.Float, types.Double,
457 types.Decimal
461 // 7.6.3 Logical negation operator
463 predefined_operators [(int) Operator.LogicalNot] = new TypeSpec [] {
464 types.Bool
468 // 7.6.4 Bitwise complement operator
470 predefined_operators [(int) Operator.OnesComplement] = new TypeSpec [] {
471 types.Int, types.UInt,
472 types.Long, types.ULong
475 return predefined_operators;
479 // Unary numeric promotions
481 static Expression DoNumericPromotion (ResolveContext rc, Operator op, Expression expr)
483 TypeSpec expr_type = expr.Type;
484 if (op == Operator.UnaryPlus || op == Operator.UnaryNegation || op == Operator.OnesComplement) {
485 switch (expr_type.BuiltinType) {
486 case BuiltinTypeSpec.Type.Byte:
487 case BuiltinTypeSpec.Type.SByte:
488 case BuiltinTypeSpec.Type.Short:
489 case BuiltinTypeSpec.Type.UShort:
490 case BuiltinTypeSpec.Type.Char:
491 return Convert.ImplicitNumericConversion (expr, rc.BuiltinTypes.Int);
495 if (op == Operator.UnaryNegation && expr_type.BuiltinType == BuiltinTypeSpec.Type.UInt)
496 return Convert.ImplicitNumericConversion (expr, rc.BuiltinTypes.Long);
498 return expr;
501 protected override Expression DoResolve (ResolveContext ec)
503 if (Oper == Operator.AddressOf) {
504 return ResolveAddressOf (ec);
507 Expr = Expr.Resolve (ec);
508 if (Expr == null)
509 return null;
511 if (Expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
512 Arguments args = new Arguments (1);
513 args.Add (new Argument (Expr));
514 return new DynamicUnaryConversion (GetOperatorExpressionTypeName (), args, loc).Resolve (ec);
517 if (Expr.Type.IsNullableType)
518 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
521 // Attempt to use a constant folding operation.
523 Constant cexpr = Expr as Constant;
524 if (cexpr != null) {
525 cexpr = TryReduceConstant (ec, cexpr);
526 if (cexpr != null)
527 return cexpr;
530 Expression expr = ResolveOperator (ec, Expr);
531 if (expr == null)
532 Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type);
535 // Reduce unary operator on predefined types
537 if (expr == this && Oper == Operator.UnaryPlus)
538 return Expr;
540 return expr;
543 public override Expression DoResolveLValue (ResolveContext ec, Expression right)
545 return null;
548 public override void Emit (EmitContext ec)
550 EmitOperator (ec, type);
553 protected void EmitOperator (EmitContext ec, TypeSpec type)
555 switch (Oper) {
556 case Operator.UnaryPlus:
557 Expr.Emit (ec);
558 break;
560 case Operator.UnaryNegation:
561 if (ec.HasSet (EmitContext.Options.CheckedScope) && !IsFloat (type)) {
562 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Expr.ContainsEmitWithAwait ())
563 Expr = Expr.EmitToField (ec);
565 ec.EmitInt (0);
566 if (type.BuiltinType == BuiltinTypeSpec.Type.Long)
567 ec.Emit (OpCodes.Conv_U8);
568 Expr.Emit (ec);
569 ec.Emit (OpCodes.Sub_Ovf);
570 } else {
571 Expr.Emit (ec);
572 ec.Emit (OpCodes.Neg);
575 break;
577 case Operator.LogicalNot:
578 Expr.Emit (ec);
579 ec.EmitInt (0);
580 ec.Emit (OpCodes.Ceq);
581 break;
583 case Operator.OnesComplement:
584 Expr.Emit (ec);
585 ec.Emit (OpCodes.Not);
586 break;
588 case Operator.AddressOf:
589 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
590 break;
592 default:
593 throw new Exception ("This should not happen: Operator = "
594 + Oper.ToString ());
598 // Same trick as in Binary expression
600 if (enum_conversion != 0) {
601 using (ec.With (BuilderContext.Options.CheckedScope, false)) {
602 ConvCast.Emit (ec, enum_conversion);
607 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
609 if (Oper == Operator.LogicalNot)
610 Expr.EmitBranchable (ec, target, !on_true);
611 else
612 base.EmitBranchable (ec, target, on_true);
615 public override void EmitSideEffect (EmitContext ec)
617 Expr.EmitSideEffect (ec);
620 public static void Error_Ambiguous (ResolveContext rc, string oper, TypeSpec type, Location loc)
622 rc.Report.Error (35, loc, "Operator `{0}' is ambiguous on an operand of type `{1}'",
623 oper, type.GetSignatureForError ());
626 public override void FlowAnalysis (FlowAnalysisContext fc)
628 FlowAnalysis (fc, false);
631 public override void FlowAnalysisConditional (FlowAnalysisContext fc)
633 FlowAnalysis (fc, true);
636 void FlowAnalysis (FlowAnalysisContext fc, bool conditional)
638 if (Oper == Operator.AddressOf) {
639 var vr = Expr as VariableReference;
640 if (vr != null && vr.VariableInfo != null)
641 fc.SetVariableAssigned (vr.VariableInfo);
643 return;
646 if (Oper == Operator.LogicalNot && conditional) {
647 Expr.FlowAnalysisConditional (fc);
649 var temp = fc.DefiniteAssignmentOnTrue;
650 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse;
651 fc.DefiniteAssignmentOnFalse = temp;
652 } else {
653 Expr.FlowAnalysis (fc);
658 // Converts operator to System.Linq.Expressions.ExpressionType enum name
660 string GetOperatorExpressionTypeName ()
662 switch (Oper) {
663 case Operator.OnesComplement:
664 return "OnesComplement";
665 case Operator.LogicalNot:
666 return "Not";
667 case Operator.UnaryNegation:
668 return "Negate";
669 case Operator.UnaryPlus:
670 return "UnaryPlus";
671 default:
672 throw new NotImplementedException ("Unknown express type operator " + Oper.ToString ());
676 static bool IsFloat (TypeSpec t)
678 return t.BuiltinType == BuiltinTypeSpec.Type.Double || t.BuiltinType == BuiltinTypeSpec.Type.Float;
682 // Returns a stringified representation of the Operator
684 public static string OperName (Operator oper)
686 switch (oper) {
687 case Operator.UnaryPlus:
688 return "+";
689 case Operator.UnaryNegation:
690 return "-";
691 case Operator.LogicalNot:
692 return "!";
693 case Operator.OnesComplement:
694 return "~";
695 case Operator.AddressOf:
696 return "&";
699 throw new NotImplementedException (oper.ToString ());
702 public override SLE.Expression MakeExpression (BuilderContext ctx)
704 var expr = Expr.MakeExpression (ctx);
705 bool is_checked = ctx.HasSet (BuilderContext.Options.CheckedScope);
707 switch (Oper) {
708 case Operator.UnaryNegation:
709 return is_checked ? SLE.Expression.NegateChecked (expr) : SLE.Expression.Negate (expr);
710 case Operator.LogicalNot:
711 return SLE.Expression.Not (expr);
712 case Operator.OnesComplement:
713 return SLE.Expression.OnesComplement (expr);
714 default:
715 throw new NotImplementedException (Oper.ToString ());
719 Expression ResolveAddressOf (ResolveContext ec)
721 if (ec.CurrentIterator != null) {
722 UnsafeInsideIteratorError (ec, loc);
723 } else if (!ec.IsUnsafe) {
724 UnsafeError (ec, loc);
727 Expr = Expr.DoResolveLValue (ec, EmptyExpression.UnaryAddress);
728 if (Expr == null || Expr.eclass != ExprClass.Variable) {
729 ec.Report.Error (211, loc, "Cannot take the address of the given expression");
730 return null;
733 if (!TypeManager.VerifyUnmanaged (ec.Module, Expr.Type, loc)) {
734 return null;
737 IVariableReference vr = Expr as IVariableReference;
738 bool is_fixed;
739 if (vr != null) {
740 is_fixed = vr.IsFixed;
741 vr.SetHasAddressTaken ();
743 if (vr.IsHoisted && ec.CurrentIterator == null) {
744 AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, vr, loc);
746 } else {
747 IFixedExpression fe = Expr as IFixedExpression;
748 is_fixed = fe != null && fe.IsFixed;
751 if (!is_fixed && !ec.HasSet (ResolveContext.Options.FixedInitializerScope)) {
752 ec.Report.Error (212, loc, "You can only take the address of unfixed expression inside of a fixed statement initializer");
755 type = PointerContainer.MakeType (ec.Module, Expr.Type);
756 eclass = ExprClass.Value;
757 return this;
760 Expression ResolvePrimitivePredefinedType (ResolveContext rc, Expression expr, TypeSpec[] predefined)
762 expr = DoNumericPromotion (rc, Oper, expr);
763 TypeSpec expr_type = expr.Type;
764 foreach (TypeSpec t in predefined) {
765 if (t == expr_type)
766 return expr;
768 return null;
772 // Perform user-operator overload resolution
774 protected virtual Expression ResolveUserOperator (ResolveContext ec, Expression expr)
776 CSharp.Operator.OpType op_type;
777 switch (Oper) {
778 case Operator.LogicalNot:
779 op_type = CSharp.Operator.OpType.LogicalNot; break;
780 case Operator.OnesComplement:
781 op_type = CSharp.Operator.OpType.OnesComplement; break;
782 case Operator.UnaryNegation:
783 op_type = CSharp.Operator.OpType.UnaryNegation; break;
784 case Operator.UnaryPlus:
785 op_type = CSharp.Operator.OpType.UnaryPlus; break;
786 default:
787 throw new InternalErrorException (Oper.ToString ());
790 var methods = MemberCache.GetUserOperator (expr.Type, op_type, false);
791 if (methods == null)
792 return null;
794 Arguments args = new Arguments (1);
795 args.Add (new Argument (expr));
797 var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc);
798 var oper = res.ResolveOperator (ec, ref args);
800 if (oper == null)
801 return null;
803 Expr = args [0].Expr;
804 return new UserOperatorCall (oper, args, CreateExpressionTree, expr.Location);
808 // Unary user type overload resolution
810 Expression ResolveUserType (ResolveContext ec, Expression expr, TypeSpec[] predefined)
812 Expression best_expr = ResolveUserOperator (ec, expr);
813 if (best_expr != null)
814 return best_expr;
816 foreach (TypeSpec t in predefined) {
817 Expression oper_expr = Convert.ImplicitUserConversion (ec, expr, t, expr.Location);
818 if (oper_expr == null)
819 continue;
821 if (oper_expr == ErrorExpression.Instance)
822 return oper_expr;
825 // decimal type is predefined but has user-operators
827 if (oper_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal)
828 oper_expr = ResolveUserType (ec, oper_expr, predefined);
829 else
830 oper_expr = ResolvePrimitivePredefinedType (ec, oper_expr, predefined);
832 if (oper_expr == null)
833 continue;
835 if (best_expr == null) {
836 best_expr = oper_expr;
837 continue;
840 int result = OverloadResolver.BetterTypeConversion (ec, best_expr.Type, t);
841 if (result == 0) {
842 if ((oper_expr is UserOperatorCall || oper_expr is UserCast) && (best_expr is UserOperatorCall || best_expr is UserCast)) {
843 Error_Ambiguous (ec, OperName (Oper), expr.Type, loc);
844 } else {
845 Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), expr.Type);
848 break;
851 if (result == 2)
852 best_expr = oper_expr;
855 if (best_expr == null)
856 return null;
859 // HACK: Decimal user-operator is included in standard operators
861 if (best_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal)
862 return best_expr;
864 Expr = best_expr;
865 type = best_expr.Type;
866 return this;
869 protected override void CloneTo (CloneContext clonectx, Expression t)
871 Unary target = (Unary) t;
873 target.Expr = Expr.Clone (clonectx);
876 public override object Accept (StructuralVisitor visitor)
878 return visitor.Visit (this);
884 // Unary operators are turned into Indirection expressions
885 // after semantic analysis (this is so we can take the address
886 // of an indirection).
888 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IFixedExpression {
889 Expression expr;
890 LocalTemporary temporary;
891 bool prepared;
893 public Indirection (Expression expr, Location l)
895 this.expr = expr;
896 loc = l;
899 public Expression Expr {
900 get {
901 return expr;
905 public bool IsFixed {
906 get { return true; }
909 public override Location StartLocation {
910 get {
911 return expr.StartLocation;
915 protected override void CloneTo (CloneContext clonectx, Expression t)
917 Indirection target = (Indirection) t;
918 target.expr = expr.Clone (clonectx);
921 public override bool ContainsEmitWithAwait ()
923 throw new NotImplementedException ();
926 public override Expression CreateExpressionTree (ResolveContext ec)
928 Error_PointerInsideExpressionTree (ec);
929 return null;
932 public override void Emit (EmitContext ec)
934 if (!prepared)
935 expr.Emit (ec);
937 ec.EmitLoadFromPtr (Type);
940 public void Emit (EmitContext ec, bool leave_copy)
942 Emit (ec);
943 if (leave_copy) {
944 ec.Emit (OpCodes.Dup);
945 temporary = new LocalTemporary (expr.Type);
946 temporary.Store (ec);
950 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
952 prepared = isCompound;
954 expr.Emit (ec);
956 if (isCompound)
957 ec.Emit (OpCodes.Dup);
959 source.Emit (ec);
960 if (leave_copy) {
961 ec.Emit (OpCodes.Dup);
962 temporary = new LocalTemporary (source.Type);
963 temporary.Store (ec);
966 ec.EmitStoreFromPtr (type);
968 if (temporary != null) {
969 temporary.Emit (ec);
970 temporary.Release (ec);
974 public void AddressOf (EmitContext ec, AddressOp Mode)
976 expr.Emit (ec);
979 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
981 return DoResolve (ec);
984 protected override Expression DoResolve (ResolveContext ec)
986 expr = expr.Resolve (ec);
987 if (expr == null)
988 return null;
990 if (ec.CurrentIterator != null) {
991 UnsafeInsideIteratorError (ec, loc);
992 } else if (!ec.IsUnsafe) {
993 UnsafeError (ec, loc);
996 var pc = expr.Type as PointerContainer;
998 if (pc == null) {
999 ec.Report.Error (193, loc, "The * or -> operator must be applied to a pointer");
1000 return null;
1003 type = pc.Element;
1005 if (type.Kind == MemberKind.Void) {
1006 Error_VoidPointerOperation (ec);
1007 return null;
1010 eclass = ExprClass.Variable;
1011 return this;
1014 public override object Accept (StructuralVisitor visitor)
1016 return visitor.Visit (this);
1020 /// <summary>
1021 /// Unary Mutator expressions (pre and post ++ and --)
1022 /// </summary>
1024 /// <remarks>
1025 /// UnaryMutator implements ++ and -- expressions. It derives from
1026 /// ExpressionStatement becuase the pre/post increment/decrement
1027 /// operators can be used in a statement context.
1029 /// FIXME: Idea, we could split this up in two classes, one simpler
1030 /// for the common case, and one with the extra fields for more complex
1031 /// classes (indexers require temporary access; overloaded require method)
1033 /// </remarks>
1034 public class UnaryMutator : ExpressionStatement
1036 class DynamicPostMutator : Expression, IAssignMethod
1038 LocalTemporary temp;
1039 Expression expr;
1041 public DynamicPostMutator (Expression expr)
1043 this.expr = expr;
1044 this.type = expr.Type;
1045 this.loc = expr.Location;
1048 public override Expression CreateExpressionTree (ResolveContext ec)
1050 throw new NotImplementedException ("ET");
1053 protected override Expression DoResolve (ResolveContext rc)
1055 eclass = expr.eclass;
1056 return this;
1059 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
1061 expr.DoResolveLValue (ec, right_side);
1062 return DoResolve (ec);
1065 public override void Emit (EmitContext ec)
1067 temp.Emit (ec);
1070 public void Emit (EmitContext ec, bool leave_copy)
1072 throw new NotImplementedException ();
1076 // Emits target assignment using unmodified source value
1078 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
1081 // Allocate temporary variable to keep original value before it's modified
1083 temp = new LocalTemporary (type);
1084 expr.Emit (ec);
1085 temp.Store (ec);
1087 ((IAssignMethod) expr).EmitAssign (ec, source, false, isCompound);
1089 if (leave_copy)
1090 Emit (ec);
1092 temp.Release (ec);
1093 temp = null;
1097 [Flags]
1098 public enum Mode : byte {
1099 IsIncrement = 0,
1100 IsDecrement = 1,
1101 IsPre = 0,
1102 IsPost = 2,
1104 PreIncrement = 0,
1105 PreDecrement = IsDecrement,
1106 PostIncrement = IsPost,
1107 PostDecrement = IsPost | IsDecrement
1110 Mode mode;
1111 bool is_expr, recurse;
1113 protected Expression expr;
1115 // Holds the real operation
1116 Expression operation;
1118 public UnaryMutator (Mode m, Expression e, Location loc)
1120 mode = m;
1121 this.loc = loc;
1122 expr = e;
1125 public Mode UnaryMutatorMode {
1126 get {
1127 return mode;
1131 public Expression Expr {
1132 get {
1133 return expr;
1137 public override Location StartLocation {
1138 get {
1139 return (mode & Mode.IsPost) != 0 ? expr.Location : loc;
1143 public override bool ContainsEmitWithAwait ()
1145 return expr.ContainsEmitWithAwait ();
1148 public override Expression CreateExpressionTree (ResolveContext ec)
1150 return new SimpleAssign (this, this).CreateExpressionTree (ec);
1153 public static TypeSpec[] CreatePredefinedOperatorsTable (BuiltinTypes types)
1156 // Predefined ++ and -- operators exist for the following types:
1157 // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal
1159 return new TypeSpec[] {
1160 types.Int,
1161 types.Long,
1163 types.SByte,
1164 types.Byte,
1165 types.Short,
1166 types.UInt,
1167 types.ULong,
1168 types.Char,
1169 types.Float,
1170 types.Double,
1171 types.Decimal
1175 protected override Expression DoResolve (ResolveContext ec)
1177 expr = expr.Resolve (ec);
1179 if (expr == null || expr.Type == InternalType.ErrorType)
1180 return null;
1182 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1184 // Handle postfix unary operators using local
1185 // temporary variable
1187 if ((mode & Mode.IsPost) != 0)
1188 expr = new DynamicPostMutator (expr);
1190 Arguments args = new Arguments (1);
1191 args.Add (new Argument (expr));
1192 return new SimpleAssign (expr, new DynamicUnaryConversion (GetOperatorExpressionTypeName (), args, loc)).Resolve (ec);
1195 if (expr.Type.IsNullableType)
1196 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
1198 return DoResolveOperation (ec);
1201 protected Expression DoResolveOperation (ResolveContext ec)
1203 eclass = ExprClass.Value;
1204 type = expr.Type;
1206 if (expr is RuntimeValueExpression) {
1207 operation = expr;
1208 } else {
1209 // Use itself at the top of the stack
1210 operation = new EmptyExpression (type);
1214 // The operand of the prefix/postfix increment decrement operators
1215 // should be an expression that is classified as a variable,
1216 // a property access or an indexer access
1218 // TODO: Move to parser, expr is ATypeNameExpression
1219 if (expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess) {
1220 expr = expr.ResolveLValue (ec, expr);
1221 } else {
1222 ec.Report.Error (1059, loc, "The operand of an increment or decrement operator must be a variable, property or indexer");
1223 return null;
1227 // Step 1: Try to find a user operator, it has priority over predefined ones
1229 var user_op = IsDecrement ? Operator.OpType.Decrement : Operator.OpType.Increment;
1230 var methods = MemberCache.GetUserOperator (type, user_op, false);
1232 if (methods != null) {
1233 Arguments args = new Arguments (1);
1234 args.Add (new Argument (expr));
1236 var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc);
1237 var method = res.ResolveOperator (ec, ref args);
1238 if (method == null)
1239 return null;
1241 args[0].Expr = operation;
1242 operation = new UserOperatorCall (method, args, null, loc);
1243 operation = Convert.ImplicitConversionRequired (ec, operation, type, loc);
1244 return this;
1248 // Step 2: Try predefined types
1251 Expression source = null;
1252 bool primitive_type;
1255 // Predefined without user conversion first for speed-up
1257 // Predefined ++ and -- operators exist for the following types:
1258 // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal
1260 switch (type.BuiltinType) {
1261 case BuiltinTypeSpec.Type.Byte:
1262 case BuiltinTypeSpec.Type.SByte:
1263 case BuiltinTypeSpec.Type.Short:
1264 case BuiltinTypeSpec.Type.UShort:
1265 case BuiltinTypeSpec.Type.Int:
1266 case BuiltinTypeSpec.Type.UInt:
1267 case BuiltinTypeSpec.Type.Long:
1268 case BuiltinTypeSpec.Type.ULong:
1269 case BuiltinTypeSpec.Type.Char:
1270 case BuiltinTypeSpec.Type.Float:
1271 case BuiltinTypeSpec.Type.Double:
1272 case BuiltinTypeSpec.Type.Decimal:
1273 source = operation;
1274 primitive_type = true;
1275 break;
1276 default:
1277 primitive_type = false;
1279 // ++/-- on pointer variables of all types except void*
1280 if (type.IsPointer) {
1281 if (((PointerContainer) type).Element.Kind == MemberKind.Void) {
1282 Error_VoidPointerOperation (ec);
1283 return null;
1286 source = operation;
1287 } else {
1288 Expression best_source = null;
1289 foreach (var t in ec.BuiltinTypes.OperatorsUnaryMutator) {
1290 source = Convert.ImplicitUserConversion (ec, operation, t, loc);
1292 // LAMESPEC: It should error on ambiguous operators but that would make us incompatible
1293 if (source == null)
1294 continue;
1296 if (best_source == null) {
1297 best_source = source;
1298 continue;
1301 var better = OverloadResolver.BetterTypeConversion (ec, best_source.Type, source.Type);
1302 if (better == 1)
1303 continue;
1305 if (better == 2) {
1306 best_source = source;
1307 continue;
1310 Unary.Error_Ambiguous (ec, OperName (mode), type, loc);
1311 break;
1314 source = best_source;
1317 // ++/-- on enum types
1318 if (source == null && type.IsEnum)
1319 source = operation;
1321 if (source == null) {
1322 expr.Error_OperatorCannotBeApplied (ec, loc, Operator.GetName (user_op), type);
1323 return null;
1326 break;
1329 var one = new IntConstant (ec.BuiltinTypes, 1, loc);
1330 var op = IsDecrement ? Binary.Operator.Subtraction : Binary.Operator.Addition;
1331 operation = new Binary (op, source, one);
1332 operation = operation.Resolve (ec);
1333 if (operation == null)
1334 throw new NotImplementedException ("should not be reached");
1336 if (operation.Type != type) {
1337 if (primitive_type)
1338 operation = Convert.ExplicitNumericConversion (ec, operation, type);
1339 else
1340 operation = Convert.ImplicitConversionRequired (ec, operation, type, loc);
1343 return this;
1346 void EmitCode (EmitContext ec, bool is_expr)
1348 recurse = true;
1349 this.is_expr = is_expr;
1350 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1353 public override void Emit (EmitContext ec)
1356 // We use recurse to allow ourselfs to be the source
1357 // of an assignment. This little hack prevents us from
1358 // having to allocate another expression
1360 if (recurse) {
1361 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1363 EmitOperation (ec);
1365 recurse = false;
1366 return;
1369 EmitCode (ec, true);
1372 protected virtual void EmitOperation (EmitContext ec)
1374 operation.Emit (ec);
1377 public override void EmitStatement (EmitContext ec)
1379 EmitCode (ec, false);
1382 public override void FlowAnalysis (FlowAnalysisContext fc)
1384 expr.FlowAnalysis (fc);
1388 // Converts operator to System.Linq.Expressions.ExpressionType enum name
1390 string GetOperatorExpressionTypeName ()
1392 return IsDecrement ? "Decrement" : "Increment";
1395 bool IsDecrement {
1396 get { return (mode & Mode.IsDecrement) != 0; }
1400 public override SLE.Expression MakeExpression (BuilderContext ctx)
1402 var target = ((RuntimeValueExpression) expr).MetaObject.Expression;
1403 var source = SLE.Expression.Convert (operation.MakeExpression (ctx), target.Type);
1404 return SLE.Expression.Assign (target, source);
1407 public static string OperName (Mode oper)
1409 return (oper & Mode.IsDecrement) != 0 ? "--" : "++";
1412 protected override void CloneTo (CloneContext clonectx, Expression t)
1414 UnaryMutator target = (UnaryMutator) t;
1416 target.expr = expr.Clone (clonectx);
1419 public override object Accept (StructuralVisitor visitor)
1421 return visitor.Visit (this);
1427 // Base class for the `is' and `as' operators
1429 public abstract class Probe : Expression
1431 public Expression ProbeType;
1432 protected Expression expr;
1433 protected TypeSpec probe_type_expr;
1435 protected Probe (Expression expr, Expression probe_type, Location l)
1437 ProbeType = probe_type;
1438 loc = l;
1439 this.expr = expr;
1442 public Expression Expr {
1443 get {
1444 return expr;
1448 public override bool ContainsEmitWithAwait ()
1450 return expr.ContainsEmitWithAwait ();
1453 protected Expression ResolveCommon (ResolveContext rc)
1455 expr = expr.Resolve (rc);
1456 if (expr == null)
1457 return null;
1459 ResolveProbeType (rc);
1460 if (probe_type_expr == null)
1461 return this;
1463 if (probe_type_expr.IsStatic) {
1464 rc.Report.Error (7023, loc, "The second operand of `is' or `as' operator cannot be static type `{0}'",
1465 probe_type_expr.GetSignatureForError ());
1466 return null;
1469 if (expr.Type.IsPointer || probe_type_expr.IsPointer) {
1470 rc.Report.Error (244, loc, "The `{0}' operator cannot be applied to an operand of pointer type",
1471 OperatorName);
1472 return null;
1475 if (expr.Type == InternalType.AnonymousMethod || expr.Type == InternalType.MethodGroup) {
1476 rc.Report.Error (837, loc, "The `{0}' operator cannot be applied to a lambda expression, anonymous method, or method group",
1477 OperatorName);
1478 return null;
1481 if (expr.Type == InternalType.DefaultType) {
1482 Error_OperatorCannotBeApplied (rc, loc, OperatorName, expr.Type);
1483 return null;
1486 return this;
1489 protected virtual void ResolveProbeType (ResolveContext rc)
1491 probe_type_expr = ProbeType.ResolveAsType (rc);
1494 public override void EmitSideEffect (EmitContext ec)
1496 expr.EmitSideEffect (ec);
1499 public override void EmitPrepare (EmitContext ec)
1501 expr.EmitPrepare (ec);
1504 public override void FlowAnalysis (FlowAnalysisContext fc)
1506 expr.FlowAnalysis (fc);
1509 public override bool HasConditionalAccess ()
1511 return expr.HasConditionalAccess ();
1514 protected abstract string OperatorName { get; }
1516 protected override void CloneTo (CloneContext clonectx, Expression t)
1518 Probe target = (Probe) t;
1520 target.expr = expr.Clone (clonectx);
1521 target.ProbeType = ProbeType.Clone (clonectx);
1526 /// <summary>
1527 /// Implementation of the `is' operator.
1528 /// </summary>
1529 public class Is : Probe
1531 Nullable.Unwrap expr_unwrap;
1532 MethodSpec number_mg;
1533 Arguments number_args;
1535 public Is (Expression expr, Expression probe_type, Location l)
1536 : base (expr, probe_type, l)
1540 protected override string OperatorName {
1541 get { return "is"; }
1544 public LocalVariable Variable { get; set; }
1546 public override Expression CreateExpressionTree (ResolveContext ec)
1548 if (Variable != null)
1549 ec.Report.Error (8122, loc, "An expression tree cannot contain a pattern matching operator");
1551 Arguments args = Arguments.CreateForExpressionTree (ec, null,
1552 expr.CreateExpressionTree (ec),
1553 new TypeOf (probe_type_expr, loc));
1555 return CreateExpressionFactoryCall (ec, "TypeIs", args);
1558 Expression CreateConstantResult (ResolveContext rc, bool result)
1560 if (result)
1561 rc.Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
1562 probe_type_expr.GetSignatureForError ());
1563 else
1564 rc.Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
1565 probe_type_expr.GetSignatureForError ());
1567 var c = new BoolConstant (rc.BuiltinTypes, result, loc);
1568 return expr.IsSideEffectFree ?
1569 ReducedExpression.Create (c, this) :
1570 new SideEffectConstant (c, this, loc);
1573 public override void Emit (EmitContext ec)
1575 if (probe_type_expr == null) {
1576 if (ProbeType is WildcardPattern) {
1577 expr.EmitSideEffect (ec);
1578 ProbeType.Emit (ec);
1579 } else {
1580 EmitPatternMatch (ec);
1582 return;
1585 EmitLoad (ec);
1587 if (expr_unwrap == null) {
1588 ec.EmitNull ();
1589 ec.Emit (OpCodes.Cgt_Un);
1593 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
1595 if (probe_type_expr == null) {
1596 EmitPatternMatch (ec);
1597 } else {
1598 EmitLoad (ec);
1601 ec.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1604 public override void EmitPrepare (EmitContext ec)
1606 base.EmitPrepare (ec);
1608 if (Variable != null)
1609 Variable.CreateBuilder (ec);
1612 void EmitPatternMatch (EmitContext ec)
1614 var no_match = ec.DefineLabel ();
1615 var end = ec.DefineLabel ();
1617 if (expr_unwrap != null) {
1618 expr_unwrap.EmitCheck (ec);
1620 if (ProbeType.IsNull) {
1621 ec.EmitInt (0);
1622 ec.Emit (OpCodes.Ceq);
1623 return;
1626 ec.Emit (OpCodes.Brfalse_S, no_match);
1627 expr_unwrap.Emit (ec);
1628 ProbeType.Emit (ec);
1629 ec.Emit (OpCodes.Ceq);
1630 ec.Emit (OpCodes.Br_S, end);
1631 ec.MarkLabel (no_match);
1632 ec.EmitInt (0);
1633 ec.MarkLabel (end);
1634 return;
1637 if (number_args != null && number_args.Count == 3) {
1638 var ce = new CallEmitter ();
1639 ce.Emit (ec, number_mg, number_args, loc);
1640 return;
1643 var probe_type = ProbeType.Type;
1645 Expr.Emit (ec);
1646 ec.Emit (OpCodes.Isinst, probe_type);
1647 ec.Emit (OpCodes.Dup);
1648 ec.Emit (OpCodes.Brfalse, no_match);
1650 bool complex_pattern = ProbeType is ComplexPatternExpression;
1651 Label prev = ec.RecursivePatternLabel;
1652 if (complex_pattern)
1653 ec.RecursivePatternLabel = ec.DefineLabel ();
1655 if (number_mg != null) {
1656 var ce = new CallEmitter ();
1657 ce.Emit (ec, number_mg, number_args, loc);
1658 } else {
1659 if (TypeSpec.IsValueType (probe_type))
1660 ec.Emit (OpCodes.Unbox_Any, probe_type);
1662 ProbeType.Emit (ec);
1663 if (complex_pattern) {
1664 ec.EmitInt (1);
1665 } else {
1666 ec.Emit (OpCodes.Ceq);
1669 ec.Emit (OpCodes.Br_S, end);
1670 ec.MarkLabel (no_match);
1672 ec.Emit (OpCodes.Pop);
1674 if (complex_pattern)
1675 ec.MarkLabel (ec.RecursivePatternLabel);
1677 ec.RecursivePatternLabel = prev;
1679 ec.EmitInt (0);
1680 ec.MarkLabel (end);
1683 void EmitLoad (EmitContext ec)
1685 if (expr_unwrap != null) {
1686 expr_unwrap.EmitCheck (ec);
1688 if (Variable == null)
1689 return;
1691 ec.Emit (OpCodes.Dup);
1692 var no_value_label = ec.DefineLabel ();
1693 ec.Emit (OpCodes.Brfalse_S, no_value_label);
1695 if (Variable.HoistedVariant != null)
1696 ec.EmitThis ();
1698 expr_unwrap.Emit (ec);
1700 if (Variable.HoistedVariant != null) {
1701 Variable.HoistedVariant.EmitAssignFromStack (ec);
1702 } else {
1704 // It's ok to have variable builder created out of order. It simplifies emit
1705 // of statements like while (condition) { }
1707 if (!Variable.Created)
1708 Variable.CreateBuilder (ec);
1710 Variable.EmitAssign (ec);
1713 ec.MarkLabel (no_value_label);
1714 return;
1717 expr.Emit (ec);
1719 bool vtype_variable = Variable != null && (probe_type_expr.IsGenericParameter || TypeSpec.IsValueType (ProbeType.Type));
1720 LocalBuilder expr_copy = null;
1722 if (vtype_variable && !ExpressionAnalyzer.IsInexpensiveLoad (expr)) {
1723 expr_copy = ec.GetTemporaryLocal (expr.Type);
1724 ec.Emit (OpCodes.Stloc, expr_copy);
1725 ec.Emit (OpCodes.Ldloc, expr_copy);
1726 } else if (probe_type_expr.IsGenericParameter && TypeSpec.IsValueType (expr.Type)) {
1728 // Only to make verifier happy
1730 ec.Emit (OpCodes.Box, expr.Type);
1733 ec.Emit (OpCodes.Isinst, probe_type_expr);
1735 if (Variable != null) {
1736 ec.Emit (OpCodes.Dup);
1738 var nonmatching_label = ec.DefineLabel ();
1739 ec.Emit (OpCodes.Brfalse_S, nonmatching_label);
1741 if (vtype_variable) {
1742 if (expr_copy != null) {
1743 ec.Emit (OpCodes.Ldloc, expr_copy);
1744 ec.FreeTemporaryLocal (expr_copy, expr.Type);
1745 } else {
1746 expr.Emit (ec);
1749 ec.Emit (OpCodes.Unbox_Any, probe_type_expr);
1750 } else {
1751 // Already on the stack
1754 if (Variable.HoistedVariant != null) {
1755 var temp = new LocalTemporary (ProbeType.Type);
1756 temp.Store (ec);
1757 Variable.HoistedVariant.EmitAssign (ec, temp, false, false);
1758 temp.Release (ec);
1760 if (!vtype_variable)
1761 Variable.HoistedVariant.Emit (ec);
1762 } else {
1764 // It's ok to have variable builder created out of order. It simplifies emit
1765 // of statements like while (condition) { }
1767 if (!Variable.Created)
1768 Variable.CreateBuilder (ec);
1770 Variable.EmitAssign (ec);
1772 if (!vtype_variable)
1773 Variable.Emit (ec);
1776 ec.MarkLabel (nonmatching_label);
1780 protected override Expression DoResolve (ResolveContext rc)
1782 if (ResolveCommon (rc) == null)
1783 return null;
1785 type = rc.BuiltinTypes.Bool;
1786 eclass = ExprClass.Value;
1788 if (probe_type_expr == null)
1789 return ResolveMatchingExpression (rc);
1791 var res = ResolveResultExpression (rc);
1792 if (Variable != null) {
1793 if (res is Constant)
1794 throw new NotImplementedException ("constant in type pattern matching");
1796 Variable.Type = probe_type_expr;
1797 var bc = rc as BlockContext;
1798 if (bc != null)
1799 Variable.PrepareAssignmentAnalysis (bc);
1802 return res;
1805 public override void FlowAnalysis (FlowAnalysisContext fc)
1807 base.FlowAnalysis (fc);
1809 if (Variable != null)
1810 fc.SetVariableAssigned (Variable.VariableInfo, true);
1813 public override void FlowAnalysisConditional (FlowAnalysisContext fc)
1815 if (Variable == null) {
1816 base.FlowAnalysisConditional (fc);
1817 return;
1820 expr.FlowAnalysis (fc);
1822 fc.DefiniteAssignmentOnTrue = fc.BranchDefiniteAssignment ();
1823 fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
1825 fc.SetVariableAssigned (Variable.VariableInfo, fc.DefiniteAssignmentOnTrue);
1828 protected override void ResolveProbeType (ResolveContext rc)
1830 if (!(ProbeType is TypeExpr) && rc.Module.Compiler.Settings.Version == LanguageVersion.Experimental) {
1831 if (ProbeType is PatternExpression) {
1832 ProbeType.Resolve (rc);
1833 return;
1837 // Have to use session recording because we don't have reliable type probing
1838 // mechanism (similar issue as in attributes resolving)
1840 // TODO: This is still wrong because ResolveAsType can be destructive
1842 var type_printer = new SessionReportPrinter ();
1843 var prev_recorder = rc.Report.SetPrinter (type_printer);
1845 probe_type_expr = ProbeType.ResolveAsType (rc);
1846 type_printer.EndSession ();
1848 if (probe_type_expr != null) {
1849 type_printer.Merge (rc.Report.Printer);
1850 rc.Report.SetPrinter (prev_recorder);
1851 return;
1854 var vexpr = ProbeType as VarExpr;
1855 if (vexpr != null && vexpr.InferType (rc, expr)) {
1856 probe_type_expr = vexpr.Type;
1857 rc.Report.SetPrinter (prev_recorder);
1858 return;
1861 var expr_printer = new SessionReportPrinter ();
1862 rc.Report.SetPrinter (expr_printer);
1863 ProbeType = ProbeType.Resolve (rc);
1864 expr_printer.EndSession ();
1866 if (ProbeType != null) {
1867 expr_printer.Merge (rc.Report.Printer);
1868 } else {
1869 type_printer.Merge (rc.Report.Printer);
1872 rc.Report.SetPrinter (prev_recorder);
1873 return;
1876 base.ResolveProbeType (rc);
1879 Expression ResolveMatchingExpression (ResolveContext rc)
1881 var mc = ProbeType as Constant;
1882 if (mc != null) {
1883 if (!Convert.ImplicitConversionExists (rc, ProbeType, Expr.Type)) {
1884 ProbeType.Error_ValueCannotBeConverted (rc, Expr.Type, false);
1885 return null;
1888 if (mc.IsNull)
1889 return new Binary (Binary.Operator.Equality, Expr, mc).Resolve (rc);
1891 var c = Expr as Constant;
1892 if (c != null) {
1893 c = ConstantFold.BinaryFold (rc, Binary.Operator.Equality, c, mc, loc);
1894 if (c != null)
1895 return c;
1898 if (Expr.Type.IsNullableType) {
1899 expr_unwrap = new Nullable.Unwrap (Expr);
1900 expr_unwrap.Resolve (rc);
1901 ProbeType = Convert.ImplicitConversion (rc, ProbeType, expr_unwrap.Type, loc);
1902 } else if (ProbeType.Type == Expr.Type) {
1903 // TODO: Better error handling
1904 return new Binary (Binary.Operator.Equality, Expr, mc, loc).Resolve (rc);
1905 } else if (ProbeType.Type.IsEnum || (ProbeType.Type.BuiltinType >= BuiltinTypeSpec.Type.Byte && ProbeType.Type.BuiltinType <= BuiltinTypeSpec.Type.Decimal)) {
1906 var helper = rc.Module.CreatePatterMatchingHelper ();
1907 number_mg = helper.NumberMatcher.Spec;
1910 // There are actually 3 arguments but the first one is already on the stack
1912 number_args = new Arguments (3);
1913 if (!ProbeType.Type.IsEnum)
1914 number_args.Add (new Argument (Expr));
1916 number_args.Add (new Argument (Convert.ImplicitConversion (rc, ProbeType, rc.BuiltinTypes.Object, loc)));
1917 number_args.Add (new Argument (new BoolLiteral (rc.BuiltinTypes, ProbeType.Type.IsEnum, loc)));
1920 return this;
1923 if (ProbeType is PatternExpression) {
1924 if (!(ProbeType is WildcardPattern) && !Convert.ImplicitConversionExists (rc, ProbeType, Expr.Type)) {
1925 ProbeType.Error_ValueCannotBeConverted (rc, Expr.Type, false);
1928 return this;
1931 // TODO: Better error message
1932 rc.Report.Error (150, ProbeType.Location, "A constant value is expected");
1933 return this;
1936 Expression ResolveResultExpression (ResolveContext ec)
1938 if (Variable != null) {
1939 if (expr is NullLiteral) {
1940 ec.Report.Error (8117, loc, "Cannot use null as pattern matching operand");
1941 return this;
1944 CheckExpressionVariable (ec);
1947 TypeSpec d = expr.Type;
1948 bool d_is_nullable = false;
1951 // If E is a method group or the null literal, or if the type of E is a reference
1952 // type or a nullable type and the value of E is null, the result is false
1954 if (expr.IsNull)
1955 return CreateConstantResult (ec, false);
1957 if (d.IsNullableType) {
1958 var ut = Nullable.NullableInfo.GetUnderlyingType (d);
1959 if (!ut.IsGenericParameter) {
1960 d = ut;
1961 d_is_nullable = true;
1965 TypeSpec t = probe_type_expr;
1966 bool t_is_nullable = false;
1967 if (t.IsNullableType) {
1968 if (Variable != null) {
1969 ec.Report.Error (8116, loc, "The nullable type `{0}' pattern matching is not allowed. Consider using underlying type `{1}'",
1970 t.GetSignatureForError (), Nullable.NullableInfo.GetUnderlyingType (t).GetSignatureForError ());
1973 var ut = Nullable.NullableInfo.GetUnderlyingType (t);
1974 if (!ut.IsGenericParameter) {
1975 t = ut;
1976 t_is_nullable = true;
1980 if (t.IsStruct) {
1981 if (d == t) {
1983 // D and T are the same value types but D can be null
1985 if (d_is_nullable && !t_is_nullable) {
1986 expr_unwrap = Nullable.Unwrap.Create (expr, true);
1987 return this;
1991 // The result is true if D and T are the same value types
1993 return CreateConstantResult (ec, true);
1996 var tp = d as TypeParameterSpec;
1997 if (tp != null)
1998 return ResolveGenericParameter (ec, t, tp);
2001 // An unboxing conversion exists
2003 if (Convert.ExplicitReferenceConversionExists (d, t))
2004 return this;
2007 // open generic type
2009 if (d is InflatedTypeSpec && InflatedTypeSpec.ContainsTypeParameter (d))
2010 return this;
2011 } else {
2012 var tps = t as TypeParameterSpec;
2013 if (tps != null)
2014 return ResolveGenericParameter (ec, d, tps);
2016 if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
2017 if (Variable != null) {
2018 ec.Report.Error (8208, loc, "The type `{0}' pattern matching is not allowed", t.GetSignatureForError ());
2019 } else {
2020 ec.Report.Warning (1981, 3, loc,
2021 "Using `{0}' to test compatibility with `{1}' is identical to testing compatibility with `object'",
2022 OperatorName, t.GetSignatureForError ());
2026 if (TypeManager.IsGenericParameter (d))
2027 return ResolveGenericParameter (ec, t, (TypeParameterSpec) d);
2029 if (TypeSpec.IsValueType (d)) {
2030 if (Convert.ImplicitBoxingConversion (null, d, t) != null) {
2031 if (d_is_nullable && !t_is_nullable) {
2032 expr_unwrap = Nullable.Unwrap.Create (expr, false);
2033 return this;
2036 return CreateConstantResult (ec, true);
2038 } else {
2039 if (Convert.ImplicitReferenceConversionExists (d, t)) {
2040 var c = expr as Constant;
2041 if (c != null)
2042 return CreateConstantResult (ec, !c.IsNull);
2045 // Do not optimize for imported type or dynamic type
2047 if (d.MemberDefinition.IsImported && d.BuiltinType != BuiltinTypeSpec.Type.None &&
2048 d.MemberDefinition.DeclaringAssembly != t.MemberDefinition.DeclaringAssembly) {
2049 return this;
2052 if (d.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
2053 return this;
2056 // Turn is check into simple null check for implicitly convertible reference types
2058 return ReducedExpression.Create (
2059 new Binary (Binary.Operator.Inequality, expr, new NullLiteral (loc), Binary.State.UserOperatorsExcluded).Resolve (ec),
2060 this).Resolve (ec);
2063 if (Convert.ExplicitReferenceConversionExists (d, t))
2064 return this;
2067 // open generic type
2069 if ((d is InflatedTypeSpec || d.IsArray) && InflatedTypeSpec.ContainsTypeParameter (d))
2070 return this;
2074 return CreateConstantResult (ec, false);
2077 Expression ResolveGenericParameter (ResolveContext ec, TypeSpec d, TypeParameterSpec t)
2079 if (t.IsReferenceType) {
2080 if (d.IsStruct)
2081 return CreateConstantResult (ec, false);
2084 if (expr.Type.IsGenericParameter) {
2085 if (expr.Type == d && TypeSpec.IsValueType (t) && TypeSpec.IsValueType (d))
2086 return CreateConstantResult (ec, true);
2088 expr = new BoxedCast (expr, d);
2091 return this;
2094 public override object Accept (StructuralVisitor visitor)
2096 return visitor.Visit (this);
2100 class WildcardPattern : PatternExpression
2102 public WildcardPattern (Location loc)
2103 : base (loc)
2107 protected override Expression DoResolve (ResolveContext rc)
2109 eclass = ExprClass.Value;
2110 type = rc.BuiltinTypes.Object;
2111 return this;
2114 public override void Emit (EmitContext ec)
2116 ec.EmitInt (1);
2120 class RecursivePattern : ComplexPatternExpression
2122 MethodGroupExpr operator_mg;
2123 Arguments operator_args;
2125 public RecursivePattern (ATypeNameExpression typeExpresion, Arguments arguments, Location loc)
2126 : base (typeExpresion, loc)
2128 Arguments = arguments;
2131 public Arguments Arguments { get; private set; }
2133 protected override Expression DoResolve (ResolveContext rc)
2135 type = TypeExpression.ResolveAsType (rc);
2136 if (type == null)
2137 return null;
2139 var operators = MemberCache.GetUserOperator (type, Operator.OpType.Is, true);
2140 if (operators == null) {
2141 Error_TypeDoesNotContainDefinition (rc, type, Operator.GetName (Operator.OpType.Is) + " operator");
2142 return null;
2145 var ops = FindMatchingOverloads (operators);
2146 if (ops == null) {
2147 // TODO: better error message
2148 Error_TypeDoesNotContainDefinition (rc, type, Operator.GetName (Operator.OpType.Is) + " operator");
2149 return null;
2152 bool dynamic_args;
2153 Arguments.Resolve (rc, out dynamic_args);
2154 if (dynamic_args)
2155 throw new NotImplementedException ("dynamic argument");
2157 var op = FindBestOverload (rc, ops);
2158 if (op == null) {
2159 // TODO: better error message
2160 Error_TypeDoesNotContainDefinition (rc, type, Operator.GetName (Operator.OpType.Is) + " operator");
2161 return null;
2164 var op_types = op.Parameters.Types;
2165 operator_args = new Arguments (op_types.Length);
2166 operator_args.Add (new Argument (new EmptyExpression (type)));
2168 for (int i = 0; i < Arguments.Count; ++i) {
2169 // TODO: Needs releasing optimization
2170 var lt = new LocalTemporary (op_types [i + 1]);
2171 operator_args.Add (new Argument (lt, Argument.AType.Out));
2173 if (comparisons == null)
2174 comparisons = new Expression[Arguments.Count];
2176 int arg_comp_index;
2177 Expression expr;
2179 var arg = Arguments [i];
2180 var named = arg as NamedArgument;
2181 if (named != null) {
2182 arg_comp_index = op.Parameters.GetParameterIndexByName (named.Name) - 1;
2183 expr = Arguments [arg_comp_index].Expr;
2184 } else {
2185 arg_comp_index = i;
2186 expr = arg.Expr;
2189 comparisons [arg_comp_index] = ResolveComparison (rc, expr, lt);
2192 operator_mg = MethodGroupExpr.CreatePredefined (op, type, loc);
2194 eclass = ExprClass.Value;
2195 return this;
2198 List<MethodSpec> FindMatchingOverloads (IList<MemberSpec> members)
2200 int arg_count = Arguments.Count + 1;
2201 List<MethodSpec> best = null;
2202 foreach (MethodSpec method in members) {
2203 var pm = method.Parameters;
2204 if (pm.Count != arg_count)
2205 continue;
2207 // TODO: Needs more thorough operator checks elsewhere to avoid doing this every time
2208 bool ok = true;
2209 for (int ii = 1; ii < pm.Count; ++ii) {
2210 if ((pm.FixedParameters [ii].ModFlags & Parameter.Modifier.OUT) == 0) {
2211 ok = false;
2212 break;
2216 if (!ok)
2217 continue;
2219 if (best == null)
2220 best = new List<MethodSpec> ();
2222 best.Add (method);
2225 return best;
2228 MethodSpec FindBestOverload (ResolveContext rc, List<MethodSpec> methods)
2230 for (int ii = 0; ii < Arguments.Count; ++ii) {
2231 var arg = Arguments [ii];
2232 var expr = arg.Expr;
2233 if (expr is WildcardPattern)
2234 continue;
2236 var na = arg as NamedArgument;
2237 for (int i = 0; i < methods.Count; ++i) {
2238 var pd = methods [i].Parameters;
2240 int index;
2241 if (na != null) {
2242 index = pd.GetParameterIndexByName (na.Name);
2243 if (index < 1) {
2244 methods.RemoveAt (i--);
2245 continue;
2247 } else {
2248 index = ii + 1;
2251 var m = pd.Types [index];
2252 if (!Convert.ImplicitConversionExists (rc, expr, m))
2253 methods.RemoveAt (i--);
2257 if (methods.Count != 1)
2258 return null;
2260 return methods [0];
2263 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
2265 operator_mg.EmitCall (ec, operator_args, false);
2266 ec.Emit (OpCodes.Brfalse, target);
2268 base.EmitBranchable (ec, target, on_true);
2271 static Expression ResolveComparison (ResolveContext rc, Expression expr, LocalTemporary lt)
2273 if (expr is WildcardPattern)
2274 return new EmptyExpression (expr.Type);
2276 var recursive = expr as RecursivePattern;
2277 expr = Convert.ImplicitConversionRequired (rc, expr, lt.Type, expr.Location);
2278 if (expr == null)
2279 return null;
2281 if (recursive != null) {
2282 recursive.SetParentInstance (lt);
2283 return expr;
2286 // TODO: Better error handling
2287 return new Binary (Binary.Operator.Equality, lt, expr, expr.Location).Resolve (rc);
2290 public void SetParentInstance (Expression instance)
2292 operator_args [0] = new Argument (instance);
2296 class PropertyPattern : ComplexPatternExpression
2298 LocalTemporary instance;
2300 public PropertyPattern (ATypeNameExpression typeExpresion, List<PropertyPatternMember> members, Location loc)
2301 : base (typeExpresion, loc)
2303 Members = members;
2306 public List<PropertyPatternMember> Members { get; private set; }
2308 protected override Expression DoResolve (ResolveContext rc)
2310 type = TypeExpression.ResolveAsType (rc);
2311 if (type == null)
2312 return null;
2314 comparisons = new Expression[Members.Count];
2316 // TODO: optimize when source is VariableReference, it'd save dup+pop
2317 instance = new LocalTemporary (type);
2319 for (int i = 0; i < Members.Count; i++) {
2320 var lookup = Members [i];
2322 var member = MemberLookup (rc, false, type, lookup.Name, 0, Expression.MemberLookupRestrictions.ExactArity, loc);
2323 if (member == null) {
2324 member = MemberLookup (rc, true, type, lookup.Name, 0, Expression.MemberLookupRestrictions.ExactArity, loc);
2325 if (member != null) {
2326 Expression.ErrorIsInaccesible (rc, member.GetSignatureForError (), loc);
2327 continue;
2331 if (member == null) {
2332 Expression.Error_TypeDoesNotContainDefinition (rc, Location, Type, lookup.Name);
2333 continue;
2336 var pe = member as PropertyExpr;
2337 if (pe == null || member is FieldExpr) {
2338 rc.Report.Error (-2001, lookup.Location, "`{0}' is not a valid pattern member", lookup.Name);
2339 continue;
2342 // TODO: Obsolete checks
2343 // TODO: check accessibility
2344 if (pe != null && !pe.PropertyInfo.HasGet) {
2345 rc.Report.Error (-2002, lookup.Location, "Property `{0}.get' accessor is required", pe.GetSignatureForError ());
2346 continue;
2349 var expr = lookup.Expr.Resolve (rc);
2350 if (expr == null)
2351 continue;
2353 var me = (MemberExpr)member;
2354 me.InstanceExpression = instance;
2356 comparisons [i] = ResolveComparison (rc, expr, me);
2359 eclass = ExprClass.Value;
2360 return this;
2363 static Expression ResolveComparison (ResolveContext rc, Expression expr, Expression instance)
2365 if (expr is WildcardPattern)
2366 return new EmptyExpression (expr.Type);
2368 return new Is (instance, expr, expr.Location).Resolve (rc);
2371 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
2373 instance.Store (ec);
2375 base.EmitBranchable (ec, target, on_true);
2379 class PropertyPatternMember
2381 public PropertyPatternMember (string name, Expression expr, Location loc)
2383 Name = name;
2384 Expr = expr;
2385 Location = loc;
2388 public string Name { get; private set; }
2389 public Expression Expr { get; private set; }
2390 public Location Location { get; private set; }
2393 abstract class PatternExpression : Expression
2395 protected PatternExpression (Location loc)
2397 this.loc = loc;
2400 public override Expression CreateExpressionTree (ResolveContext ec)
2402 throw new NotImplementedException ();
2406 abstract class ComplexPatternExpression : PatternExpression
2408 protected Expression[] comparisons;
2410 protected ComplexPatternExpression (ATypeNameExpression typeExpresion, Location loc)
2411 : base (loc)
2413 TypeExpression = typeExpresion;
2416 public ATypeNameExpression TypeExpression { get; private set; }
2418 public override void Emit (EmitContext ec)
2420 EmitBranchable (ec, ec.RecursivePatternLabel, false);
2423 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
2425 if (comparisons != null) {
2426 foreach (var comp in comparisons) {
2427 comp.EmitBranchable (ec, target, false);
2433 /// <summary>
2434 /// Implementation of the `as' operator.
2435 /// </summary>
2436 public class As : Probe {
2438 public As (Expression expr, Expression probe_type, Location l)
2439 : base (expr, probe_type, l)
2443 protected override string OperatorName {
2444 get { return "as"; }
2447 public override Expression CreateExpressionTree (ResolveContext ec)
2449 Arguments args = Arguments.CreateForExpressionTree (ec, null,
2450 expr.CreateExpressionTree (ec),
2451 new TypeOf (probe_type_expr, loc));
2453 return CreateExpressionFactoryCall (ec, "TypeAs", args);
2456 public override void Emit (EmitContext ec)
2458 expr.Emit (ec);
2460 ec.Emit (OpCodes.Isinst, type);
2462 if (TypeManager.IsGenericParameter (type) || type.IsNullableType)
2463 ec.Emit (OpCodes.Unbox_Any, type);
2466 protected override Expression DoResolve (ResolveContext ec)
2468 if (ResolveCommon (ec) == null)
2469 return null;
2471 type = probe_type_expr;
2472 eclass = ExprClass.Value;
2473 TypeSpec etype = expr.Type;
2475 if (expr is TupleLiteral && TupleLiteral.ContainsNoTypeElement (etype)) {
2476 ec.Report.Error (8307, expr.Location, "The first operand of an `as' operator may not be a tuple literal without a natural type");
2477 type = InternalType.ErrorType;
2478 return this;
2481 if (type == null) {
2482 type = InternalType.ErrorType;
2483 return this;
2486 if (!TypeSpec.IsReferenceType (type) && !type.IsNullableType) {
2487 if (TypeManager.IsGenericParameter (type)) {
2488 ec.Report.Error (413, loc,
2489 "The `as' operator cannot be used with a non-reference type parameter `{0}'. Consider adding `class' or a reference type constraint",
2490 probe_type_expr.GetSignatureForError ());
2491 } else {
2492 ec.Report.Error (77, loc,
2493 "The `as' operator cannot be used with a non-nullable value type `{0}'",
2494 type.GetSignatureForError ());
2496 return null;
2499 if (expr.IsNull && type.IsNullableType) {
2500 return Nullable.LiftedNull.CreateFromExpression (ec, this);
2503 // If the compile-time type of E is dynamic, unlike the cast operator the as operator is not dynamically bound
2504 if (etype.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
2505 return this;
2508 Expression e = Convert.ImplicitConversionStandard (ec, expr, type, loc);
2509 if (e != null) {
2510 e = EmptyCast.Create (e, type);
2511 return ReducedExpression.Create (e, this).Resolve (ec);
2514 if (Convert.ExplicitReferenceConversionExists (etype, type)){
2515 if (TypeManager.IsGenericParameter (etype))
2516 expr = new BoxedCast (expr, etype);
2518 return this;
2521 if (InflatedTypeSpec.ContainsTypeParameter (etype) || InflatedTypeSpec.ContainsTypeParameter (type)) {
2522 expr = new BoxedCast (expr, etype);
2523 return this;
2526 if (etype != InternalType.ErrorType) {
2527 ec.Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
2528 etype.GetSignatureForError (), type.GetSignatureForError ());
2531 return null;
2534 public override object Accept (StructuralVisitor visitor)
2536 return visitor.Visit (this);
2541 // This represents a typecast in the source language.
2543 public class Cast : ShimExpression {
2544 Expression target_type;
2546 public Cast (Expression cast_type, Expression expr, Location loc)
2547 : base (expr)
2549 this.target_type = cast_type;
2550 this.loc = loc;
2553 public Expression TargetType {
2554 get { return target_type; }
2557 protected override Expression DoResolve (ResolveContext ec)
2559 expr = expr.Resolve (ec);
2560 if (expr == null)
2561 return null;
2563 type = target_type.ResolveAsType (ec);
2564 if (type == null)
2565 return null;
2567 if (type.IsStatic) {
2568 ec.Report.Error (716, loc, "Cannot convert to static type `{0}'", type.GetSignatureForError ());
2569 return null;
2572 if (type.IsPointer) {
2573 if (ec.CurrentIterator != null) {
2574 UnsafeInsideIteratorError (ec, loc);
2575 } else if (!ec.IsUnsafe) {
2576 UnsafeError (ec, loc);
2580 eclass = ExprClass.Value;
2582 Constant c = expr as Constant;
2583 if (c != null) {
2584 c = c.Reduce (ec, type);
2585 if (c != null)
2586 return c;
2589 var res = Convert.ExplicitConversion (ec, expr, type, loc);
2590 if (res == expr)
2591 return EmptyCast.Create (res, type);
2593 return res;
2596 protected override void CloneTo (CloneContext clonectx, Expression t)
2598 Cast target = (Cast) t;
2600 target.target_type = target_type.Clone (clonectx);
2601 target.expr = expr.Clone (clonectx);
2604 public override object Accept (StructuralVisitor visitor)
2606 return visitor.Visit (this);
2610 public class ImplicitCast : ShimExpression
2612 bool arrayAccess;
2614 public ImplicitCast (Expression expr, TypeSpec target, bool arrayAccess)
2615 : base (expr)
2617 this.loc = expr.Location;
2618 this.type = target;
2619 this.arrayAccess = arrayAccess;
2622 protected override Expression DoResolve (ResolveContext ec)
2624 expr = expr.Resolve (ec);
2625 if (expr == null)
2626 return null;
2628 if (arrayAccess)
2629 expr = ConvertExpressionToArrayIndex (ec, expr);
2630 else
2631 expr = Convert.ImplicitConversionRequired (ec, expr, type, loc);
2633 return expr;
2637 public class DeclarationExpression : Expression, IMemoryLocation
2639 LocalVariableReference lvr;
2641 public DeclarationExpression (FullNamedExpression variableType, LocalVariable variable)
2643 VariableType = variableType;
2644 Variable = variable;
2645 this.loc = variable.Location;
2648 public LocalVariable Variable { get; set; }
2649 public Expression Initializer { get; set; }
2650 public FullNamedExpression VariableType { get; set; }
2652 public void AddressOf (EmitContext ec, AddressOp mode)
2654 if (!Variable.Created)
2655 Variable.CreateBuilder (ec);
2657 if (Initializer != null) {
2658 lvr.EmitAssign (ec, Initializer, false, false);
2661 lvr.AddressOf (ec, mode);
2664 protected override void CloneTo (CloneContext clonectx, Expression t)
2666 var target = (DeclarationExpression) t;
2668 target.VariableType = (FullNamedExpression) VariableType.Clone (clonectx);
2670 if (Initializer != null)
2671 target.Initializer = Initializer.Clone (clonectx);
2674 public override Expression CreateExpressionTree (ResolveContext rc)
2676 rc.Report.Error (8198, loc, "An expression tree cannot contain out variable declaration");
2677 return null;
2680 bool DoResolveCommon (ResolveContext rc)
2682 CheckExpressionVariable (rc);
2684 var var_expr = VariableType as VarExpr;
2685 if (var_expr != null) {
2686 type = InternalType.VarOutType;
2687 } else {
2688 type = VariableType.ResolveAsType (rc);
2689 if (type == null)
2690 return false;
2693 if (Initializer != null) {
2694 Initializer = Initializer.Resolve (rc);
2696 if (var_expr != null && Initializer != null && var_expr.InferType (rc, Initializer)) {
2697 type = var_expr.Type;
2701 Variable.Type = type;
2702 lvr = new LocalVariableReference (Variable, loc);
2704 eclass = ExprClass.Variable;
2705 return true;
2708 protected override Expression DoResolve (ResolveContext rc)
2710 if (DoResolveCommon (rc))
2711 lvr.Resolve (rc);
2713 return this;
2716 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
2718 if (lvr == null && DoResolveCommon (rc))
2719 lvr.ResolveLValue (rc, right_side);
2721 return this;
2724 public override void Emit (EmitContext ec)
2726 throw new NotImplementedException ();
2729 public override void EmitPrepare (EmitContext ec)
2731 Variable.CreateBuilder (ec);
2736 // C# 2.0 Default value expression
2738 public class DefaultValueExpression : Expression
2740 Expression expr;
2742 public DefaultValueExpression (Expression expr, Location loc)
2744 this.expr = expr;
2745 this.loc = loc;
2748 public Expression Expr {
2749 get {
2750 return this.expr;
2754 public override bool IsSideEffectFree {
2755 get {
2756 return true;
2760 public override bool ContainsEmitWithAwait ()
2762 return false;
2765 public override Expression CreateExpressionTree (ResolveContext ec)
2767 Arguments args = new Arguments (2);
2768 args.Add (new Argument (this));
2769 args.Add (new Argument (new TypeOf (type, loc)));
2770 return CreateExpressionFactoryCall (ec, "Constant", args);
2773 protected override Expression DoResolve (ResolveContext ec)
2775 type = expr.ResolveAsType (ec);
2776 if (type == null)
2777 return null;
2779 if (type.IsStatic) {
2780 ec.Report.Error (-244, loc, "The `default value' operator cannot be applied to an operand of a static type");
2783 if (type.IsPointer)
2784 return new NullLiteral (Location).ConvertImplicitly (type);
2786 if (TypeSpec.IsReferenceType (type))
2787 return new NullConstant (type, loc);
2789 Constant c = New.Constantify (type, expr.Location);
2790 if (c != null)
2791 return c;
2793 eclass = ExprClass.Variable;
2794 return this;
2797 public override void Emit (EmitContext ec)
2799 LocalTemporary temp_storage = new LocalTemporary(type);
2801 temp_storage.AddressOf(ec, AddressOp.LoadStore);
2802 ec.Emit(OpCodes.Initobj, type);
2803 temp_storage.Emit(ec);
2804 temp_storage.Release (ec);
2807 #if !STATIC
2808 public override SLE.Expression MakeExpression (BuilderContext ctx)
2810 return SLE.Expression.Default (type.GetMetaInfo ());
2812 #endif
2814 protected override void CloneTo (CloneContext clonectx, Expression t)
2816 DefaultValueExpression target = (DefaultValueExpression) t;
2818 target.expr = expr.Clone (clonectx);
2821 public override object Accept (StructuralVisitor visitor)
2823 return visitor.Visit (this);
2827 /// <summary>
2828 /// Binary operators
2829 /// </summary>
2830 public class Binary : Expression, IDynamicBinder
2832 public class PredefinedOperator
2834 protected readonly TypeSpec left;
2835 protected readonly TypeSpec right;
2836 protected readonly TypeSpec left_unwrap;
2837 protected readonly TypeSpec right_unwrap;
2838 public readonly Operator OperatorsMask;
2839 public TypeSpec ReturnType;
2841 public PredefinedOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask)
2842 : this (ltype, rtype, op_mask, ltype)
2846 public PredefinedOperator (TypeSpec type, Operator op_mask, TypeSpec return_type)
2847 : this (type, type, op_mask, return_type)
2851 public PredefinedOperator (TypeSpec type, Operator op_mask)
2852 : this (type, type, op_mask, type)
2856 public PredefinedOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec return_type)
2858 if ((op_mask & Operator.ValuesOnlyMask) != 0)
2859 throw new InternalErrorException ("Only masked values can be used");
2861 if ((op_mask & Operator.NullableMask) != 0) {
2862 left_unwrap = Nullable.NullableInfo.GetUnderlyingType (ltype);
2863 right_unwrap = Nullable.NullableInfo.GetUnderlyingType (rtype);
2864 } else {
2865 left_unwrap = ltype;
2866 right_unwrap = rtype;
2869 this.left = ltype;
2870 this.right = rtype;
2871 this.OperatorsMask = op_mask;
2872 this.ReturnType = return_type;
2875 public bool IsLifted {
2876 get {
2877 return (OperatorsMask & Operator.NullableMask) != 0;
2881 public virtual Expression ConvertResult (ResolveContext rc, Binary b)
2883 Constant c;
2885 var left_expr = b.left;
2886 var right_expr = b.right;
2888 b.type = ReturnType;
2890 if (IsLifted) {
2891 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
2892 b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
2893 b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
2896 if (right_expr.IsNull) {
2897 if ((b.oper & Operator.EqualityMask) != 0) {
2898 if (!left_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (left_expr.Type))
2899 return b.CreateLiftedValueTypeResult (rc, left_expr.Type);
2900 } else if ((b.oper & Operator.BitwiseMask) != 0) {
2901 if (left_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool)
2902 return Nullable.LiftedNull.CreateFromExpression (rc, b);
2903 } else {
2904 b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
2905 b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
2907 if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
2908 return Nullable.LiftedNull.CreateFromExpression (rc, b);
2910 return b.CreateLiftedValueTypeResult (rc, left);
2912 } else if (left_expr.IsNull) {
2913 if ((b.oper & Operator.EqualityMask) != 0) {
2914 if (!right_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (right_expr.Type))
2915 return b.CreateLiftedValueTypeResult (rc, right_expr.Type);
2916 } else if ((b.oper & Operator.BitwiseMask) != 0) {
2917 if (right_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool)
2918 return Nullable.LiftedNull.CreateFromExpression (rc, b);
2919 } else {
2920 b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
2921 b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
2923 if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
2924 return Nullable.LiftedNull.CreateFromExpression (rc, b);
2926 return b.CreateLiftedValueTypeResult (rc, right);
2932 // A user operators does not support multiple user conversions, but decimal type
2933 // is considered to be predefined type therefore we apply predefined operators rules
2934 // and then look for decimal user-operator implementation
2936 if (left.BuiltinType == BuiltinTypeSpec.Type.Decimal) {
2937 b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
2938 b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
2940 return b.ResolveUserOperator (rc, b.left, b.right);
2943 c = right_expr as Constant;
2944 if (c != null) {
2945 if (c.IsDefaultValue) {
2947 // Optimizes
2949 // (expr + 0) to expr
2950 // (expr - 0) to expr
2951 // (bool? | false) to bool?
2953 if (b.oper == Operator.Addition || b.oper == Operator.Subtraction ||
2954 (b.oper == Operator.BitwiseOr && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) {
2955 b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
2956 return ReducedExpression.Create (b.left, b).Resolve (rc);
2960 // Optimizes (value &/&& 0) to 0
2962 if ((b.oper == Operator.BitwiseAnd || b.oper == Operator.LogicalAnd) && !IsLifted) {
2963 Constant side_effect = new SideEffectConstant (c, b.left, c.Location);
2964 return ReducedExpression.Create (side_effect, b);
2966 } else {
2968 // Optimizes (bool? & true) to bool?
2970 if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) {
2971 return ReducedExpression.Create (b.left, b).Resolve (rc);
2975 if ((b.oper == Operator.Multiply || b.oper == Operator.Division) && c.IsOneInteger)
2976 return ReducedExpression.Create (b.left, b).Resolve (rc);
2978 if ((b.oper & Operator.ShiftMask) != 0 && c is IntConstant) {
2979 b.right = new IntConstant (rc.BuiltinTypes, ((IntConstant) c).Value & GetShiftMask (left_unwrap), b.right.Location);
2983 c = b.left as Constant;
2984 if (c != null) {
2985 if (c.IsDefaultValue) {
2987 // Optimizes
2989 // (0 + expr) to expr
2990 // (false | bool?) to bool?
2992 if (b.oper == Operator.Addition ||
2993 (b.oper == Operator.BitwiseOr && right_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) {
2994 b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
2995 return ReducedExpression.Create (b.right, b).Resolve (rc);
2999 // Optimizes (false && expr) to false
3001 if (b.oper == Operator.LogicalAnd && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) {
3002 // No rhs side-effects
3003 Expression.Warning_UnreachableExpression (rc, b.right.StartLocation);
3004 return ReducedExpression.Create (c, b);
3008 // Optimizes (0 & value) to 0
3010 if (b.oper == Operator.BitwiseAnd && !IsLifted) {
3011 Constant side_effect = new SideEffectConstant (c, b.right, c.Location);
3012 return ReducedExpression.Create (side_effect, b);
3014 } else {
3016 // Optimizes (true & bool?) to bool?
3018 if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) {
3019 return ReducedExpression.Create (b.right, b).Resolve (rc);
3023 // Optimizes (true || expr) to true
3025 if (b.oper == Operator.LogicalOr && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) {
3026 // No rhs side-effects
3027 Expression.Warning_UnreachableExpression (rc, b.right.StartLocation);
3028 return ReducedExpression.Create (c, b);
3032 if (b.oper == Operator.Multiply && c.IsOneInteger)
3033 return ReducedExpression.Create (b.right, b).Resolve (rc);
3036 if (IsLifted) {
3037 var lifted = new Nullable.LiftedBinaryOperator (b);
3039 TypeSpec ltype, rtype;
3040 if (b.left.Type.IsNullableType) {
3041 lifted.UnwrapLeft = new Nullable.Unwrap (b.left);
3042 ltype = left_unwrap;
3043 } else {
3044 ltype = left;
3047 if (b.right.Type.IsNullableType) {
3048 lifted.UnwrapRight = new Nullable.Unwrap (b.right);
3049 rtype = right_unwrap;
3050 } else {
3051 rtype = right;
3054 lifted.Left = b.left.IsNull ?
3055 Nullable.LiftedNull.Create (ltype, b.left.Location) :
3056 Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? b.left, ltype, b.left.Location);
3058 lifted.Right = b.right.IsNull ?
3059 Nullable.LiftedNull.Create (rtype, b.right.Location) :
3060 Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? b.right, rtype, b.right.Location);
3062 return lifted.Resolve (rc);
3065 b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
3066 b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
3068 return b;
3071 public bool IsPrimitiveApplicable (TypeSpec ltype, TypeSpec rtype)
3074 // We are dealing with primitive types only
3076 return left == ltype && ltype == rtype;
3079 public virtual bool IsApplicable (ResolveContext ec, Expression lexpr, Expression rexpr)
3081 // Quick path
3082 if (left == lexpr.Type && right == rexpr.Type)
3083 return true;
3085 return Convert.ImplicitConversionExists (ec, lexpr, left) &&
3086 Convert.ImplicitConversionExists (ec, rexpr, right);
3089 public PredefinedOperator ResolveBetterOperator (ResolveContext ec, PredefinedOperator best_operator)
3091 if ((OperatorsMask & Operator.DecomposedMask) != 0)
3092 return best_operator;
3094 if ((best_operator.OperatorsMask & Operator.DecomposedMask) != 0)
3095 return this;
3097 int result = 0;
3098 if (left != null && best_operator.left != null) {
3099 result = OverloadResolver.BetterTypeConversion (ec, best_operator.left_unwrap, left_unwrap);
3103 // When second argument is same as the first one, the result is same
3105 if (right != null && (left != right || best_operator.left != best_operator.right)) {
3106 result |= OverloadResolver.BetterTypeConversion (ec, best_operator.right_unwrap, right_unwrap);
3109 if (result == 0 || result > 2)
3110 return null;
3112 return result == 1 ? best_operator : this;
3116 sealed class PredefinedStringOperator : PredefinedOperator
3118 public PredefinedStringOperator (TypeSpec type, Operator op_mask, TypeSpec retType)
3119 : base (type, type, op_mask, retType)
3123 public PredefinedStringOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec retType)
3124 : base (ltype, rtype, op_mask, retType)
3128 public override Expression ConvertResult (ResolveContext ec, Binary b)
3131 // Use original expression for nullable arguments
3133 Nullable.Unwrap unwrap = b.left as Nullable.Unwrap;
3134 if (unwrap != null)
3135 b.left = unwrap.Original;
3137 unwrap = b.right as Nullable.Unwrap;
3138 if (unwrap != null)
3139 b.right = unwrap.Original;
3141 b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
3142 b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
3145 // Start a new concat expression using converted expression
3147 return StringConcat.Create (ec, b.left, b.right, b.loc);
3151 sealed class PredefinedEqualityOperator : PredefinedOperator
3153 MethodSpec equal_method, inequal_method;
3155 public PredefinedEqualityOperator (TypeSpec arg, TypeSpec retType)
3156 : base (arg, arg, Operator.EqualityMask, retType)
3160 public override Expression ConvertResult (ResolveContext ec, Binary b)
3162 b.type = ReturnType;
3164 b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
3165 b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
3167 Arguments args = new Arguments (2);
3168 args.Add (new Argument (b.left));
3169 args.Add (new Argument (b.right));
3171 MethodSpec method;
3172 if (b.oper == Operator.Equality) {
3173 if (equal_method == null) {
3174 if (left.BuiltinType == BuiltinTypeSpec.Type.String)
3175 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (b.loc);
3176 else if (left.BuiltinType == BuiltinTypeSpec.Type.Delegate)
3177 equal_method = ec.Module.PredefinedMembers.DelegateEqual.Resolve (b.loc);
3178 else
3179 throw new NotImplementedException (left.GetSignatureForError ());
3182 method = equal_method;
3183 } else {
3184 if (inequal_method == null) {
3185 if (left.BuiltinType == BuiltinTypeSpec.Type.String)
3186 inequal_method = ec.Module.PredefinedMembers.StringInequal.Resolve (b.loc);
3187 else if (left.BuiltinType == BuiltinTypeSpec.Type.Delegate)
3188 inequal_method = ec.Module.PredefinedMembers.DelegateInequal.Resolve (b.loc);
3189 else
3190 throw new NotImplementedException (left.GetSignatureForError ());
3193 method = inequal_method;
3196 return new UserOperatorCall (method, args, b.CreateExpressionTree, b.loc);
3200 class PredefinedPointerOperator : PredefinedOperator
3202 public PredefinedPointerOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask)
3203 : base (ltype, rtype, op_mask)
3207 public PredefinedPointerOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec retType)
3208 : base (ltype, rtype, op_mask, retType)
3212 public PredefinedPointerOperator (TypeSpec type, Operator op_mask, TypeSpec return_type)
3213 : base (type, op_mask, return_type)
3217 public override bool IsApplicable (ResolveContext ec, Expression lexpr, Expression rexpr)
3219 if (left == null) {
3220 if (!lexpr.Type.IsPointer)
3221 return false;
3222 } else {
3223 if (!Convert.ImplicitConversionExists (ec, lexpr, left))
3224 return false;
3227 if (right == null) {
3228 if (!rexpr.Type.IsPointer)
3229 return false;
3230 } else {
3231 if (!Convert.ImplicitConversionExists (ec, rexpr, right))
3232 return false;
3235 return true;
3238 public override Expression ConvertResult (ResolveContext ec, Binary b)
3240 if (left != null) {
3241 b.left = Convert.UserDefinedConversion (ec, b.left, left, Convert.UserConversionRestriction.ImplicitOnly, b.loc) ?? EmptyCast.Create (b.left, left);
3242 } else if (right != null) {
3243 b.right = Convert.UserDefinedConversion (ec, b.right, right, Convert.UserConversionRestriction.ImplicitOnly, b.loc) ?? EmptyCast.Create (b.right, right);
3246 TypeSpec r_type = ReturnType;
3247 Expression left_arg, right_arg;
3248 if (r_type == null) {
3249 if (left == null) {
3250 left_arg = b.left;
3251 right_arg = b.right;
3252 r_type = b.left.Type;
3253 } else {
3254 left_arg = b.right;
3255 right_arg = b.left;
3256 r_type = b.right.Type;
3258 } else {
3259 left_arg = b.left;
3260 right_arg = b.right;
3263 return new PointerArithmetic (b.oper, left_arg, right_arg, r_type, b.loc).Resolve (ec);
3267 [Flags]
3268 public enum Operator {
3269 Multiply = 0 | ArithmeticMask,
3270 Division = 1 | ArithmeticMask,
3271 Modulus = 2 | ArithmeticMask,
3272 Addition = 3 | ArithmeticMask | AdditionMask,
3273 Subtraction = 4 | ArithmeticMask | SubtractionMask,
3275 LeftShift = 5 | ShiftMask,
3276 RightShift = 6 | ShiftMask,
3278 LessThan = 7 | ComparisonMask | RelationalMask,
3279 GreaterThan = 8 | ComparisonMask | RelationalMask,
3280 LessThanOrEqual = 9 | ComparisonMask | RelationalMask,
3281 GreaterThanOrEqual = 10 | ComparisonMask | RelationalMask,
3282 Equality = 11 | ComparisonMask | EqualityMask,
3283 Inequality = 12 | ComparisonMask | EqualityMask,
3285 BitwiseAnd = 13 | BitwiseMask,
3286 ExclusiveOr = 14 | BitwiseMask,
3287 BitwiseOr = 15 | BitwiseMask,
3289 LogicalAnd = 16 | LogicalMask,
3290 LogicalOr = 17 | LogicalMask,
3293 // Operator masks
3295 ValuesOnlyMask = ArithmeticMask - 1,
3296 ArithmeticMask = 1 << 5,
3297 ShiftMask = 1 << 6,
3298 ComparisonMask = 1 << 7,
3299 EqualityMask = 1 << 8,
3300 BitwiseMask = 1 << 9,
3301 LogicalMask = 1 << 10,
3302 AdditionMask = 1 << 11,
3303 SubtractionMask = 1 << 12,
3304 RelationalMask = 1 << 13,
3306 DecomposedMask = 1 << 19,
3307 NullableMask = 1 << 20
3310 [Flags]
3311 public enum State : byte
3313 None = 0,
3314 Compound = 1 << 1,
3315 UserOperatorsExcluded = 1 << 2
3318 readonly Operator oper;
3319 Expression left, right;
3320 State state;
3321 ConvCast.Mode enum_conversion;
3323 public Binary (Operator oper, Expression left, Expression right, bool isCompound)
3324 : this (oper, left, right, State.Compound)
3328 public Binary (Operator oper, Expression left, Expression right, State state)
3329 : this (oper, left, right)
3331 this.state = state;
3334 public Binary (Operator oper, Expression left, Expression right)
3335 : this (oper, left, right, left.Location)
3339 public Binary (Operator oper, Expression left, Expression right, Location loc)
3341 this.oper = oper;
3342 this.left = left;
3343 this.right = right;
3344 this.loc = loc;
3347 #region Properties
3349 public bool IsCompound {
3350 get {
3351 return (state & State.Compound) != 0;
3355 public Operator Oper {
3356 get {
3357 return oper;
3361 public Expression Left {
3362 get {
3363 return this.left;
3367 public Expression Right {
3368 get {
3369 return this.right;
3373 public override Location StartLocation {
3374 get {
3375 return left.StartLocation;
3379 #endregion
3381 /// <summary>
3382 /// Returns a stringified representation of the Operator
3383 /// </summary>
3384 string OperName (Operator oper)
3386 string s;
3387 switch (oper){
3388 case Operator.Multiply:
3389 s = "*";
3390 break;
3391 case Operator.Division:
3392 s = "/";
3393 break;
3394 case Operator.Modulus:
3395 s = "%";
3396 break;
3397 case Operator.Addition:
3398 s = "+";
3399 break;
3400 case Operator.Subtraction:
3401 s = "-";
3402 break;
3403 case Operator.LeftShift:
3404 s = "<<";
3405 break;
3406 case Operator.RightShift:
3407 s = ">>";
3408 break;
3409 case Operator.LessThan:
3410 s = "<";
3411 break;
3412 case Operator.GreaterThan:
3413 s = ">";
3414 break;
3415 case Operator.LessThanOrEqual:
3416 s = "<=";
3417 break;
3418 case Operator.GreaterThanOrEqual:
3419 s = ">=";
3420 break;
3421 case Operator.Equality:
3422 s = "==";
3423 break;
3424 case Operator.Inequality:
3425 s = "!=";
3426 break;
3427 case Operator.BitwiseAnd:
3428 s = "&";
3429 break;
3430 case Operator.BitwiseOr:
3431 s = "|";
3432 break;
3433 case Operator.ExclusiveOr:
3434 s = "^";
3435 break;
3436 case Operator.LogicalOr:
3437 s = "||";
3438 break;
3439 case Operator.LogicalAnd:
3440 s = "&&";
3441 break;
3442 default:
3443 s = oper.ToString ();
3444 break;
3447 if (IsCompound)
3448 return s + "=";
3450 return s;
3453 public static void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right, Operator oper, Location loc)
3455 new Binary (oper, left, right).Error_OperatorCannotBeApplied (ec, left, right);
3458 public static void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right, string oper, Location loc)
3460 if (left.Type == InternalType.ErrorType || right.Type == InternalType.ErrorType)
3461 return;
3463 string l, r;
3464 l = left.Type.GetSignatureForError ();
3465 r = right.Type.GetSignatureForError ();
3467 ec.Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
3468 oper, l, r);
3471 void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right)
3473 Error_OperatorCannotBeApplied (ec, left, right, OperName (oper), loc);
3476 public override void FlowAnalysis (FlowAnalysisContext fc)
3479 // Optimized version when on-true/on-false data are not needed
3481 if ((oper & Operator.LogicalMask) == 0) {
3482 left.FlowAnalysis (fc);
3483 right.FlowAnalysis (fc);
3484 return;
3487 left.FlowAnalysisConditional (fc);
3488 var left_fc_ontrue = fc.DefiniteAssignmentOnTrue;
3489 var left_fc_onfalse = fc.DefiniteAssignmentOnFalse;
3491 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet (
3492 oper == Operator.LogicalOr ? left_fc_onfalse : left_fc_ontrue);
3493 right.FlowAnalysisConditional (fc);
3495 if (oper == Operator.LogicalOr)
3496 fc.DefiniteAssignment = (left_fc_onfalse | (fc.DefiniteAssignmentOnFalse & fc.DefiniteAssignmentOnTrue)) & left_fc_ontrue;
3497 else
3498 fc.DefiniteAssignment = (left_fc_ontrue | (fc.DefiniteAssignmentOnFalse & fc.DefiniteAssignmentOnTrue)) & left_fc_onfalse;
3501 public override void FlowAnalysisConditional (FlowAnalysisContext fc)
3503 if ((oper & Operator.LogicalMask) == 0) {
3504 base.FlowAnalysisConditional (fc);
3505 return;
3508 left.FlowAnalysisConditional (fc);
3509 var left_fc_ontrue = fc.DefiniteAssignmentOnTrue;
3510 var left_fc_onfalse = fc.DefiniteAssignmentOnFalse;
3512 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet (
3513 oper == Operator.LogicalOr ? left_fc_onfalse : left_fc_ontrue);
3514 right.FlowAnalysisConditional (fc);
3516 var lc = left as Constant;
3517 if (oper == Operator.LogicalOr) {
3518 fc.DefiniteAssignmentOnFalse = left_fc_onfalse | fc.DefiniteAssignmentOnFalse;
3519 if (lc != null && lc.IsDefaultValue)
3520 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse;
3521 else
3522 fc.DefiniteAssignmentOnTrue = new DefiniteAssignmentBitSet (left_fc_ontrue & (left_fc_onfalse | fc.DefiniteAssignmentOnTrue));
3523 } else {
3524 fc.DefiniteAssignmentOnTrue = left_fc_ontrue | fc.DefiniteAssignmentOnTrue;
3525 if (lc != null && !lc.IsDefaultValue)
3526 fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignmentOnTrue;
3527 else
3528 fc.DefiniteAssignmentOnFalse = new DefiniteAssignmentBitSet ((left_fc_ontrue | fc.DefiniteAssignmentOnFalse) & left_fc_onfalse);
3533 // Converts operator to System.Linq.Expressions.ExpressionType enum name
3535 string GetOperatorExpressionTypeName ()
3537 switch (oper) {
3538 case Operator.Addition:
3539 return IsCompound ? "AddAssign" : "Add";
3540 case Operator.BitwiseAnd:
3541 return IsCompound ? "AndAssign" : "And";
3542 case Operator.BitwiseOr:
3543 return IsCompound ? "OrAssign" : "Or";
3544 case Operator.Division:
3545 return IsCompound ? "DivideAssign" : "Divide";
3546 case Operator.ExclusiveOr:
3547 return IsCompound ? "ExclusiveOrAssign" : "ExclusiveOr";
3548 case Operator.Equality:
3549 return "Equal";
3550 case Operator.GreaterThan:
3551 return "GreaterThan";
3552 case Operator.GreaterThanOrEqual:
3553 return "GreaterThanOrEqual";
3554 case Operator.Inequality:
3555 return "NotEqual";
3556 case Operator.LeftShift:
3557 return IsCompound ? "LeftShiftAssign" : "LeftShift";
3558 case Operator.LessThan:
3559 return "LessThan";
3560 case Operator.LessThanOrEqual:
3561 return "LessThanOrEqual";
3562 case Operator.LogicalAnd:
3563 return "And";
3564 case Operator.LogicalOr:
3565 return "Or";
3566 case Operator.Modulus:
3567 return IsCompound ? "ModuloAssign" : "Modulo";
3568 case Operator.Multiply:
3569 return IsCompound ? "MultiplyAssign" : "Multiply";
3570 case Operator.RightShift:
3571 return IsCompound ? "RightShiftAssign" : "RightShift";
3572 case Operator.Subtraction:
3573 return IsCompound ? "SubtractAssign" : "Subtract";
3574 default:
3575 throw new NotImplementedException ("Unknown expression type operator " + oper.ToString ());
3579 public static CSharp.Operator.OpType ConvertBinaryToUserOperator (Operator op)
3581 switch (op) {
3582 case Operator.Addition:
3583 return CSharp.Operator.OpType.Addition;
3584 case Operator.BitwiseAnd:
3585 case Operator.LogicalAnd:
3586 return CSharp.Operator.OpType.BitwiseAnd;
3587 case Operator.BitwiseOr:
3588 case Operator.LogicalOr:
3589 return CSharp.Operator.OpType.BitwiseOr;
3590 case Operator.Division:
3591 return CSharp.Operator.OpType.Division;
3592 case Operator.Equality:
3593 return CSharp.Operator.OpType.Equality;
3594 case Operator.ExclusiveOr:
3595 return CSharp.Operator.OpType.ExclusiveOr;
3596 case Operator.GreaterThan:
3597 return CSharp.Operator.OpType.GreaterThan;
3598 case Operator.GreaterThanOrEqual:
3599 return CSharp.Operator.OpType.GreaterThanOrEqual;
3600 case Operator.Inequality:
3601 return CSharp.Operator.OpType.Inequality;
3602 case Operator.LeftShift:
3603 return CSharp.Operator.OpType.LeftShift;
3604 case Operator.LessThan:
3605 return CSharp.Operator.OpType.LessThan;
3606 case Operator.LessThanOrEqual:
3607 return CSharp.Operator.OpType.LessThanOrEqual;
3608 case Operator.Modulus:
3609 return CSharp.Operator.OpType.Modulus;
3610 case Operator.Multiply:
3611 return CSharp.Operator.OpType.Multiply;
3612 case Operator.RightShift:
3613 return CSharp.Operator.OpType.RightShift;
3614 case Operator.Subtraction:
3615 return CSharp.Operator.OpType.Subtraction;
3616 default:
3617 throw new InternalErrorException (op.ToString ());
3621 public override bool ContainsEmitWithAwait ()
3623 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
3626 public static void EmitOperatorOpcode (EmitContext ec, Operator oper, TypeSpec l, Expression right)
3628 OpCode opcode;
3630 switch (oper){
3631 case Operator.Multiply:
3632 if (ec.HasSet (EmitContext.Options.CheckedScope)) {
3633 if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long)
3634 opcode = OpCodes.Mul_Ovf;
3635 else if (!IsFloat (l))
3636 opcode = OpCodes.Mul_Ovf_Un;
3637 else
3638 opcode = OpCodes.Mul;
3639 } else
3640 opcode = OpCodes.Mul;
3642 break;
3644 case Operator.Division:
3645 if (IsUnsigned (l))
3646 opcode = OpCodes.Div_Un;
3647 else
3648 opcode = OpCodes.Div;
3649 break;
3651 case Operator.Modulus:
3652 if (IsUnsigned (l))
3653 opcode = OpCodes.Rem_Un;
3654 else
3655 opcode = OpCodes.Rem;
3656 break;
3658 case Operator.Addition:
3659 if (ec.HasSet (EmitContext.Options.CheckedScope)) {
3660 if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long)
3661 opcode = OpCodes.Add_Ovf;
3662 else if (!IsFloat (l))
3663 opcode = OpCodes.Add_Ovf_Un;
3664 else
3665 opcode = OpCodes.Add;
3666 } else
3667 opcode = OpCodes.Add;
3668 break;
3670 case Operator.Subtraction:
3671 if (ec.HasSet (EmitContext.Options.CheckedScope)) {
3672 if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long)
3673 opcode = OpCodes.Sub_Ovf;
3674 else if (!IsFloat (l))
3675 opcode = OpCodes.Sub_Ovf_Un;
3676 else
3677 opcode = OpCodes.Sub;
3678 } else
3679 opcode = OpCodes.Sub;
3680 break;
3682 case Operator.RightShift:
3683 if (!(right is IntConstant)) {
3684 ec.EmitInt (GetShiftMask (l));
3685 ec.Emit (OpCodes.And);
3688 if (IsUnsigned (l))
3689 opcode = OpCodes.Shr_Un;
3690 else
3691 opcode = OpCodes.Shr;
3692 break;
3694 case Operator.LeftShift:
3695 if (!(right is IntConstant)) {
3696 ec.EmitInt (GetShiftMask (l));
3697 ec.Emit (OpCodes.And);
3700 opcode = OpCodes.Shl;
3701 break;
3703 case Operator.Equality:
3704 opcode = OpCodes.Ceq;
3705 break;
3707 case Operator.Inequality:
3708 ec.Emit (OpCodes.Ceq);
3709 ec.EmitInt (0);
3711 opcode = OpCodes.Ceq;
3712 break;
3714 case Operator.LessThan:
3715 if (IsUnsigned (l))
3716 opcode = OpCodes.Clt_Un;
3717 else
3718 opcode = OpCodes.Clt;
3719 break;
3721 case Operator.GreaterThan:
3722 if (IsUnsigned (l))
3723 opcode = OpCodes.Cgt_Un;
3724 else
3725 opcode = OpCodes.Cgt;
3726 break;
3728 case Operator.LessThanOrEqual:
3729 if (IsUnsigned (l) || IsFloat (l))
3730 ec.Emit (OpCodes.Cgt_Un);
3731 else
3732 ec.Emit (OpCodes.Cgt);
3733 ec.EmitInt (0);
3735 opcode = OpCodes.Ceq;
3736 break;
3738 case Operator.GreaterThanOrEqual:
3739 if (IsUnsigned (l) || IsFloat (l))
3740 ec.Emit (OpCodes.Clt_Un);
3741 else
3742 ec.Emit (OpCodes.Clt);
3744 ec.EmitInt (0);
3746 opcode = OpCodes.Ceq;
3747 break;
3749 case Operator.BitwiseOr:
3750 opcode = OpCodes.Or;
3751 break;
3753 case Operator.BitwiseAnd:
3754 opcode = OpCodes.And;
3755 break;
3757 case Operator.ExclusiveOr:
3758 opcode = OpCodes.Xor;
3759 break;
3761 default:
3762 throw new InternalErrorException (oper.ToString ());
3765 ec.Emit (opcode);
3768 static int GetShiftMask (TypeSpec type)
3770 return type.BuiltinType == BuiltinTypeSpec.Type.Int || type.BuiltinType == BuiltinTypeSpec.Type.UInt ? 0x1f : 0x3f;
3773 static bool IsUnsigned (TypeSpec t)
3775 switch (t.BuiltinType) {
3776 case BuiltinTypeSpec.Type.Char:
3777 case BuiltinTypeSpec.Type.UInt:
3778 case BuiltinTypeSpec.Type.ULong:
3779 case BuiltinTypeSpec.Type.UShort:
3780 case BuiltinTypeSpec.Type.Byte:
3781 return true;
3784 return t.IsPointer;
3787 static bool IsFloat (TypeSpec t)
3789 return t.BuiltinType == BuiltinTypeSpec.Type.Float || t.BuiltinType == BuiltinTypeSpec.Type.Double;
3792 public Expression ResolveOperator (ResolveContext rc)
3794 eclass = ExprClass.Value;
3796 TypeSpec l = left.Type;
3797 TypeSpec r = right.Type;
3798 Expression expr;
3799 bool primitives_only = false;
3802 // Handles predefined primitive types
3804 if ((BuiltinTypeSpec.IsPrimitiveType (l) || (l.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (l)))) &&
3805 (BuiltinTypeSpec.IsPrimitiveType (r) || (r.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (r))))) {
3806 if ((oper & Operator.ShiftMask) == 0) {
3807 if (!DoBinaryOperatorPromotion (rc))
3808 return null;
3810 primitives_only = BuiltinTypeSpec.IsPrimitiveType (l) && BuiltinTypeSpec.IsPrimitiveType (r);
3812 } else {
3813 // Pointers
3814 if (l.IsPointer || r.IsPointer)
3815 return ResolveOperatorPointer (rc, l, r);
3817 // User operators
3818 if ((state & State.UserOperatorsExcluded) == 0) {
3819 expr = ResolveUserOperator (rc, left, right);
3820 if (expr != null)
3821 return expr;
3824 bool lenum = l.IsEnum;
3825 bool renum = r.IsEnum;
3826 if ((oper & (Operator.ComparisonMask | Operator.BitwiseMask)) != 0) {
3828 // Enumerations
3830 if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) {
3831 expr = ResolveSingleEnumOperators (rc, lenum, renum, l, r);
3833 if (expr == null)
3834 return null;
3836 if ((oper & Operator.BitwiseMask) != 0) {
3837 expr = EmptyCast.Create (expr, type);
3838 enum_conversion = GetEnumResultCast (type);
3840 if (oper == Operator.BitwiseAnd && left.Type.IsEnum && right.Type.IsEnum) {
3841 expr = OptimizeAndOperation (expr);
3845 left = ConvertEnumOperandToUnderlyingType (rc, left, r.IsNullableType);
3846 right = ConvertEnumOperandToUnderlyingType (rc, right, l.IsNullableType);
3847 return expr;
3849 } else if ((oper == Operator.Addition || oper == Operator.Subtraction)) {
3850 if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) {
3852 // Enumerations
3854 expr = ResolveEnumOperators (rc, lenum, renum, l, r);
3857 // We cannot break here there is also Enum + String possible match
3858 // which is not ambiguous with predefined enum operators
3860 if (expr != null) {
3861 left = ConvertEnumOperandToUnderlyingType (rc, left, false);
3862 right = ConvertEnumOperandToUnderlyingType (rc, right, false);
3864 return expr;
3866 } else if (l.IsDelegate || r.IsDelegate) {
3868 // Delegates
3870 expr = ResolveOperatorDelegate (rc, l, r);
3872 // TODO: Can this be ambiguous
3873 if (expr != null)
3874 return expr;
3880 // Equality operators are more complicated
3882 if ((oper & Operator.EqualityMask) != 0) {
3883 return ResolveEquality (rc, l, r, primitives_only);
3886 expr = ResolveOperatorPredefined (rc, rc.BuiltinTypes.OperatorsBinaryStandard, primitives_only);
3887 if (expr != null)
3888 return expr;
3890 if (primitives_only)
3891 return null;
3894 // Lifted operators have lower priority
3896 return ResolveOperatorPredefined (rc, rc.Module.OperatorsBinaryLifted, false);
3899 static bool IsEnumOrNullableEnum (TypeSpec type)
3901 return type.IsEnum || (type.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (type).IsEnum);
3905 // at least one of 'left' or 'right' is an enumeration constant (EnumConstant or SideEffectConstant or ...)
3906 // if 'left' is not an enumeration constant, create one from the type of 'right'
3907 Constant EnumLiftUp (ResolveContext ec, Constant left, Constant right)
3909 switch (oper) {
3910 case Operator.BitwiseOr:
3911 case Operator.BitwiseAnd:
3912 case Operator.ExclusiveOr:
3913 case Operator.Equality:
3914 case Operator.Inequality:
3915 case Operator.LessThan:
3916 case Operator.LessThanOrEqual:
3917 case Operator.GreaterThan:
3918 case Operator.GreaterThanOrEqual:
3919 if (left.Type.IsEnum)
3920 return left;
3922 if (left.IsZeroInteger)
3923 return left.Reduce (ec, right.Type);
3925 break;
3927 case Operator.Addition:
3928 case Operator.Subtraction:
3929 return left;
3931 case Operator.Multiply:
3932 case Operator.Division:
3933 case Operator.Modulus:
3934 case Operator.LeftShift:
3935 case Operator.RightShift:
3936 if (right.Type.IsEnum || left.Type.IsEnum)
3937 break;
3938 return left;
3941 return null;
3945 // The `|' operator used on types which were extended is dangerous
3947 void CheckBitwiseOrOnSignExtended (ResolveContext ec)
3949 OpcodeCast lcast = left as OpcodeCast;
3950 if (lcast != null) {
3951 if (IsUnsigned (lcast.UnderlyingType))
3952 lcast = null;
3955 OpcodeCast rcast = right as OpcodeCast;
3956 if (rcast != null) {
3957 if (IsUnsigned (rcast.UnderlyingType))
3958 rcast = null;
3961 if (lcast == null && rcast == null)
3962 return;
3964 // FIXME: consider constants
3966 var ltype = lcast != null ? lcast.UnderlyingType : rcast.UnderlyingType;
3967 ec.Report.Warning (675, 3, loc,
3968 "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
3969 ltype.GetSignatureForError ());
3972 public static PredefinedOperator[] CreatePointerOperatorsTable (BuiltinTypes types)
3974 return new PredefinedOperator[] {
3976 // Pointer arithmetic:
3978 // T* operator + (T* x, int y); T* operator - (T* x, int y);
3979 // T* operator + (T* x, uint y); T* operator - (T* x, uint y);
3980 // T* operator + (T* x, long y); T* operator - (T* x, long y);
3981 // T* operator + (T* x, ulong y); T* operator - (T* x, ulong y);
3983 new PredefinedPointerOperator (null, types.Int, Operator.AdditionMask | Operator.SubtractionMask),
3984 new PredefinedPointerOperator (null, types.UInt, Operator.AdditionMask | Operator.SubtractionMask),
3985 new PredefinedPointerOperator (null, types.Long, Operator.AdditionMask | Operator.SubtractionMask),
3986 new PredefinedPointerOperator (null, types.ULong, Operator.AdditionMask | Operator.SubtractionMask),
3989 // T* operator + (int y, T* x);
3990 // T* operator + (uint y, T *x);
3991 // T* operator + (long y, T *x);
3992 // T* operator + (ulong y, T *x);
3994 new PredefinedPointerOperator (types.Int, null, Operator.AdditionMask, null),
3995 new PredefinedPointerOperator (types.UInt, null, Operator.AdditionMask, null),
3996 new PredefinedPointerOperator (types.Long, null, Operator.AdditionMask, null),
3997 new PredefinedPointerOperator (types.ULong, null, Operator.AdditionMask, null),
4000 // long operator - (T* x, T *y)
4002 new PredefinedPointerOperator (null, Operator.SubtractionMask, types.Long)
4006 public static PredefinedOperator[] CreateStandardOperatorsTable (BuiltinTypes types)
4008 TypeSpec bool_type = types.Bool;
4010 return new [] {
4011 new PredefinedOperator (types.Int, Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask),
4012 new PredefinedOperator (types.UInt, Operator.ArithmeticMask | Operator.BitwiseMask),
4013 new PredefinedOperator (types.Long, Operator.ArithmeticMask | Operator.BitwiseMask),
4014 new PredefinedOperator (types.ULong, Operator.ArithmeticMask | Operator.BitwiseMask),
4015 new PredefinedOperator (types.Float, Operator.ArithmeticMask),
4016 new PredefinedOperator (types.Double, Operator.ArithmeticMask),
4017 new PredefinedOperator (types.Decimal, Operator.ArithmeticMask),
4019 new PredefinedOperator (types.Int, Operator.ComparisonMask, bool_type),
4020 new PredefinedOperator (types.UInt, Operator.ComparisonMask, bool_type),
4021 new PredefinedOperator (types.Long, Operator.ComparisonMask, bool_type),
4022 new PredefinedOperator (types.ULong, Operator.ComparisonMask, bool_type),
4023 new PredefinedOperator (types.Float, Operator.ComparisonMask, bool_type),
4024 new PredefinedOperator (types.Double, Operator.ComparisonMask, bool_type),
4025 new PredefinedOperator (types.Decimal, Operator.ComparisonMask, bool_type),
4027 new PredefinedStringOperator (types.String, Operator.AdditionMask, types.String),
4028 // Remaining string operators are in lifted tables
4030 new PredefinedOperator (bool_type, Operator.BitwiseMask | Operator.LogicalMask | Operator.EqualityMask, bool_type),
4032 new PredefinedOperator (types.UInt, types.Int, Operator.ShiftMask),
4033 new PredefinedOperator (types.Long, types.Int, Operator.ShiftMask),
4034 new PredefinedOperator (types.ULong, types.Int, Operator.ShiftMask)
4038 public static PredefinedOperator[] CreateStandardLiftedOperatorsTable (ModuleContainer module)
4040 var types = module.Compiler.BuiltinTypes;
4043 // Not strictly lifted but need to be in second group otherwise expressions like
4044 // int + null would resolve to +(object, string) instead of +(int?, int?)
4046 var string_operators = new [] {
4047 new PredefinedStringOperator (types.String, types.Object, Operator.AdditionMask, types.String),
4048 new PredefinedStringOperator (types.Object, types.String, Operator.AdditionMask, types.String),
4051 var nullable = module.PredefinedTypes.Nullable.TypeSpec;
4052 if (nullable == null)
4053 return string_operators;
4055 var bool_type = types.Bool;
4057 var nullable_bool = nullable.MakeGenericType (module, new[] { bool_type });
4058 var nullable_int = nullable.MakeGenericType (module, new[] { types.Int });
4059 var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt });
4060 var nullable_long = nullable.MakeGenericType (module, new[] { types.Long });
4061 var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong });
4062 var nullable_float = nullable.MakeGenericType (module, new[] { types.Float });
4063 var nullable_double = nullable.MakeGenericType (module, new[] { types.Double });
4064 var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal });
4066 return new[] {
4067 new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask),
4068 new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask),
4069 new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask),
4070 new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask),
4071 new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ArithmeticMask),
4072 new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ArithmeticMask),
4073 new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ArithmeticMask),
4075 new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ComparisonMask, bool_type),
4076 new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ComparisonMask, bool_type),
4077 new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ComparisonMask, bool_type),
4078 new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ComparisonMask, bool_type),
4079 new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ComparisonMask, bool_type),
4080 new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ComparisonMask, bool_type),
4081 new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ComparisonMask, bool_type),
4083 new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.BitwiseMask, nullable_bool),
4085 new PredefinedOperator (nullable_uint, nullable_int, Operator.NullableMask | Operator.ShiftMask),
4086 new PredefinedOperator (nullable_long, nullable_int, Operator.NullableMask | Operator.ShiftMask),
4087 new PredefinedOperator (nullable_ulong, nullable_int, Operator.NullableMask | Operator.ShiftMask),
4089 string_operators [0],
4090 string_operators [1]
4094 public static PredefinedOperator[] CreateEqualityOperatorsTable (BuiltinTypes types)
4096 TypeSpec bool_type = types.Bool;
4098 return new[] {
4099 new PredefinedEqualityOperator (types.String, bool_type),
4100 new PredefinedEqualityOperator (types.Delegate, bool_type),
4101 new PredefinedOperator (bool_type, Operator.EqualityMask, bool_type),
4102 new PredefinedOperator (types.Int, Operator.EqualityMask, bool_type),
4103 new PredefinedOperator (types.UInt, Operator.EqualityMask, bool_type),
4104 new PredefinedOperator (types.Long, Operator.EqualityMask, bool_type),
4105 new PredefinedOperator (types.ULong, Operator.EqualityMask, bool_type),
4106 new PredefinedOperator (types.Float, Operator.EqualityMask, bool_type),
4107 new PredefinedOperator (types.Double, Operator.EqualityMask, bool_type),
4108 new PredefinedOperator (types.Decimal, Operator.EqualityMask, bool_type),
4112 public static PredefinedOperator[] CreateEqualityLiftedOperatorsTable (ModuleContainer module)
4114 var nullable = module.PredefinedTypes.Nullable.TypeSpec;
4116 if (nullable == null)
4117 return new PredefinedOperator [0];
4119 var types = module.Compiler.BuiltinTypes;
4120 var bool_type = types.Bool;
4121 var nullable_bool = nullable.MakeGenericType (module, new [] { bool_type });
4122 var nullable_int = nullable.MakeGenericType (module, new[] { types.Int });
4123 var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt });
4124 var nullable_long = nullable.MakeGenericType (module, new[] { types.Long });
4125 var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong });
4126 var nullable_float = nullable.MakeGenericType (module, new[] { types.Float });
4127 var nullable_double = nullable.MakeGenericType (module, new[] { types.Double });
4128 var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal });
4130 return new [] {
4131 new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.EqualityMask, bool_type),
4132 new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.EqualityMask, bool_type),
4133 new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.EqualityMask, bool_type),
4134 new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.EqualityMask, bool_type),
4135 new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.EqualityMask, bool_type),
4136 new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.EqualityMask, bool_type),
4137 new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.EqualityMask, bool_type),
4138 new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.EqualityMask, bool_type)
4143 // 7.2.6.2 Binary numeric promotions
4145 bool DoBinaryOperatorPromotion (ResolveContext rc)
4147 TypeSpec ltype = left.Type;
4148 if (ltype.IsNullableType) {
4149 ltype = Nullable.NullableInfo.GetUnderlyingType (ltype);
4153 // This is numeric promotion code only
4155 if (ltype.BuiltinType == BuiltinTypeSpec.Type.Bool)
4156 return true;
4158 TypeSpec rtype = right.Type;
4159 if (rtype.IsNullableType) {
4160 rtype = Nullable.NullableInfo.GetUnderlyingType (rtype);
4163 var lb = ltype.BuiltinType;
4164 var rb = rtype.BuiltinType;
4165 TypeSpec type;
4166 Expression expr;
4168 if (lb == BuiltinTypeSpec.Type.Decimal || rb == BuiltinTypeSpec.Type.Decimal) {
4169 type = rc.BuiltinTypes.Decimal;
4170 } else if (lb == BuiltinTypeSpec.Type.Double || rb == BuiltinTypeSpec.Type.Double) {
4171 type = rc.BuiltinTypes.Double;
4172 } else if (lb == BuiltinTypeSpec.Type.Float || rb == BuiltinTypeSpec.Type.Float) {
4173 type = rc.BuiltinTypes.Float;
4174 } else if (lb == BuiltinTypeSpec.Type.ULong || rb == BuiltinTypeSpec.Type.ULong) {
4175 type = rc.BuiltinTypes.ULong;
4177 if (IsSignedType (lb)) {
4178 expr = ConvertSignedConstant (left, type);
4179 if (expr == null)
4180 return false;
4181 left = expr;
4182 } else if (IsSignedType (rb)) {
4183 expr = ConvertSignedConstant (right, type);
4184 if (expr == null)
4185 return false;
4186 right = expr;
4189 } else if (lb == BuiltinTypeSpec.Type.Long || rb == BuiltinTypeSpec.Type.Long) {
4190 type = rc.BuiltinTypes.Long;
4191 } else if (lb == BuiltinTypeSpec.Type.UInt || rb == BuiltinTypeSpec.Type.UInt) {
4192 type = rc.BuiltinTypes.UInt;
4194 if (IsSignedType (lb)) {
4195 expr = ConvertSignedConstant (left, type);
4196 if (expr == null)
4197 type = rc.BuiltinTypes.Long;
4198 } else if (IsSignedType (rb)) {
4199 expr = ConvertSignedConstant (right, type);
4200 if (expr == null)
4201 type = rc.BuiltinTypes.Long;
4203 } else {
4204 type = rc.BuiltinTypes.Int;
4207 if (ltype != type) {
4208 expr = PromoteExpression (rc, left, type);
4209 if (expr == null)
4210 return false;
4212 left = expr;
4215 if (rtype != type) {
4216 expr = PromoteExpression (rc, right, type);
4217 if (expr == null)
4218 return false;
4220 right = expr;
4223 return true;
4226 static bool IsSignedType (BuiltinTypeSpec.Type type)
4228 switch (type) {
4229 case BuiltinTypeSpec.Type.Int:
4230 case BuiltinTypeSpec.Type.Short:
4231 case BuiltinTypeSpec.Type.SByte:
4232 case BuiltinTypeSpec.Type.Long:
4233 return true;
4234 default:
4235 return false;
4239 static Expression ConvertSignedConstant (Expression expr, TypeSpec type)
4241 var c = expr as Constant;
4242 if (c == null)
4243 return null;
4245 return c.ConvertImplicitly (type);
4248 static Expression PromoteExpression (ResolveContext rc, Expression expr, TypeSpec type)
4250 if (expr.Type.IsNullableType) {
4251 return Convert.ImplicitConversionStandard (rc, expr,
4252 rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc, new[] { type }), expr.Location);
4255 var c = expr as Constant;
4256 if (c != null)
4257 return c.ConvertImplicitly (type);
4259 return Convert.ImplicitNumericConversion (expr, type);
4262 protected override Expression DoResolve (ResolveContext ec)
4264 if (left == null)
4265 return null;
4267 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
4268 left = ((ParenthesizedExpression) left).Expr;
4269 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
4270 if (left == null)
4271 return null;
4273 if (left.eclass == ExprClass.Type) {
4274 ec.Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
4275 return null;
4277 } else
4278 left = left.Resolve (ec);
4280 if (left == null)
4281 return null;
4283 right = right.Resolve (ec);
4284 if (right == null)
4285 return null;
4287 Constant lc = left as Constant;
4288 Constant rc = right as Constant;
4290 // The conversion rules are ignored in enum context but why
4291 if (!ec.HasSet (ResolveContext.Options.EnumScope) && lc != null && rc != null && (left.Type.IsEnum || right.Type.IsEnum)) {
4292 lc = EnumLiftUp (ec, lc, rc);
4293 if (lc != null)
4294 rc = EnumLiftUp (ec, rc, lc);
4297 if (rc != null && lc != null) {
4298 int prev_e = ec.Report.Errors;
4299 Expression e = ConstantFold.BinaryFold (ec, oper, lc, rc, loc);
4300 if (e != null || ec.Report.Errors != prev_e)
4301 return e;
4304 // Comparison warnings
4305 if ((oper & Operator.ComparisonMask) != 0) {
4306 if (left.Equals (right)) {
4307 ec.Report.Warning (1718, 3, loc, "A comparison made to same variable. Did you mean to compare something else?");
4309 CheckOutOfRangeComparison (ec, lc, right.Type);
4310 CheckOutOfRangeComparison (ec, rc, left.Type);
4313 var ltype = left.Type;
4314 var rtype = right.Type;
4315 if (ltype.BuiltinType == BuiltinTypeSpec.Type.Dynamic || rtype.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
4316 return DoResolveDynamic (ec);
4319 // Only default with == and != is explicitly allowed
4321 if (ltype == InternalType.DefaultType || rtype == InternalType.DefaultType) {
4322 if ((Oper & Operator.EqualityMask) == 0) {
4323 ec.Report.Error (8310, loc, "Operator `{0}' cannot be applied to operand `default'", OperName (Oper));
4324 return null;
4327 if (ltype == rtype) {
4328 ec.Report.Error (8315, loc, "Operator `{0}' is ambiguous on operands `default' and `default'", OperName (Oper));
4329 return null;
4332 if (rtype == InternalType.DefaultType) {
4333 right = new DefaultValueExpression (new TypeExpression (ltype, right.Location), right.Location).Resolve (ec);
4334 } else {
4335 left = new DefaultValueExpression (new TypeExpression (rtype, left.Location), left.Location).Resolve (ec);
4339 return DoResolveCore (ec, left, right);
4342 Expression DoResolveDynamic (ResolveContext rc)
4344 var lt = left.Type;
4345 var rt = right.Type;
4346 if (lt.Kind == MemberKind.Void || lt == InternalType.MethodGroup || lt == InternalType.AnonymousMethod ||
4347 rt.Kind == MemberKind.Void || rt == InternalType.MethodGroup || rt == InternalType.AnonymousMethod) {
4348 Error_OperatorCannotBeApplied (rc, left, right);
4349 return null;
4352 Arguments args;
4355 // Special handling for logical boolean operators which require rhs not to be
4356 // evaluated based on lhs value
4358 if ((oper & Operator.LogicalMask) != 0) {
4359 Expression cond_left, cond_right, expr;
4361 args = new Arguments (2);
4363 if (lt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
4364 LocalVariable temp = LocalVariable.CreateCompilerGenerated (lt, rc.CurrentBlock, loc);
4366 var cond_args = new Arguments (1);
4367 cond_args.Add (new Argument (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left).Resolve (rc)));
4370 // dynamic && bool => IsFalse (temp = left) ? temp : temp && right;
4371 // dynamic || bool => IsTrue (temp = left) ? temp : temp || right;
4373 left = temp.CreateReferenceExpression (rc, loc);
4374 if (oper == Operator.LogicalAnd) {
4375 expr = DynamicUnaryConversion.CreateIsFalse (rc, cond_args, loc);
4376 cond_left = left;
4377 } else {
4378 expr = DynamicUnaryConversion.CreateIsTrue (rc, cond_args, loc);
4379 cond_left = left;
4382 args.Add (new Argument (left));
4383 args.Add (new Argument (right));
4384 cond_right = new DynamicExpressionStatement (this, args, loc);
4385 } else {
4386 LocalVariable temp = LocalVariable.CreateCompilerGenerated (rc.BuiltinTypes.Bool, rc.CurrentBlock, loc);
4388 if (!Convert.ImplicitConversionExists (rc, left, temp.Type) && (oper == Operator.LogicalAnd ? GetOperatorFalse (rc, left, loc) : GetOperatorTrue (rc, left, loc)) == null) {
4389 rc.Report.Error (7083, left.Location,
4390 "Expression must be implicitly convertible to Boolean or its type `{0}' must define operator `{1}'",
4391 lt.GetSignatureForError (), oper == Operator.LogicalAnd ? "false" : "true");
4392 return null;
4395 args.Add (new Argument (temp.CreateReferenceExpression (rc, loc).Resolve (rc)));
4396 args.Add (new Argument (right));
4397 right = new DynamicExpressionStatement (this, args, loc);
4400 // bool && dynamic => (temp = left) ? temp && right : temp;
4401 // bool || dynamic => (temp = left) ? temp : temp || right;
4403 if (oper == Operator.LogicalAnd) {
4404 cond_left = right;
4405 cond_right = temp.CreateReferenceExpression (rc, loc);
4406 } else {
4407 cond_left = temp.CreateReferenceExpression (rc, loc);
4408 cond_right = right;
4411 expr = new BooleanExpression (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left));
4414 return new Conditional (expr, cond_left, cond_right, loc).Resolve (rc);
4417 args = new Arguments (2);
4418 args.Add (new Argument (left));
4419 args.Add (new Argument (right));
4420 return new DynamicExpressionStatement (this, args, loc).Resolve (rc);
4423 Expression DoResolveCore (ResolveContext ec, Expression left_orig, Expression right_orig)
4425 Expression expr = ResolveOperator (ec);
4426 if (expr == null)
4427 Error_OperatorCannotBeApplied (ec, left_orig, right_orig);
4429 if (left == null || right == null)
4430 throw new InternalErrorException ("Invalid conversion");
4432 if (oper == Operator.BitwiseOr)
4433 CheckBitwiseOrOnSignExtended (ec);
4435 return expr;
4438 public override SLE.Expression MakeExpression (BuilderContext ctx)
4440 return MakeExpression (ctx, left, right);
4443 public SLE.Expression MakeExpression (BuilderContext ctx, Expression left, Expression right)
4445 var le = left.MakeExpression (ctx);
4446 var re = right.MakeExpression (ctx);
4447 bool is_checked = ctx.HasSet (BuilderContext.Options.CheckedScope);
4449 switch (oper) {
4450 case Operator.Addition:
4451 return is_checked ? SLE.Expression.AddChecked (le, re) : SLE.Expression.Add (le, re);
4452 case Operator.BitwiseAnd:
4453 return SLE.Expression.And (le, re);
4454 case Operator.BitwiseOr:
4455 return SLE.Expression.Or (le, re);
4456 case Operator.Division:
4457 return SLE.Expression.Divide (le, re);
4458 case Operator.Equality:
4459 return SLE.Expression.Equal (le, re);
4460 case Operator.ExclusiveOr:
4461 return SLE.Expression.ExclusiveOr (le, re);
4462 case Operator.GreaterThan:
4463 return SLE.Expression.GreaterThan (le, re);
4464 case Operator.GreaterThanOrEqual:
4465 return SLE.Expression.GreaterThanOrEqual (le, re);
4466 case Operator.Inequality:
4467 return SLE.Expression.NotEqual (le, re);
4468 case Operator.LeftShift:
4469 return SLE.Expression.LeftShift (le, re);
4470 case Operator.LessThan:
4471 return SLE.Expression.LessThan (le, re);
4472 case Operator.LessThanOrEqual:
4473 return SLE.Expression.LessThanOrEqual (le, re);
4474 case Operator.LogicalAnd:
4475 return SLE.Expression.AndAlso (le, re);
4476 case Operator.LogicalOr:
4477 return SLE.Expression.OrElse (le, re);
4478 case Operator.Modulus:
4479 return SLE.Expression.Modulo (le, re);
4480 case Operator.Multiply:
4481 return is_checked ? SLE.Expression.MultiplyChecked (le, re) : SLE.Expression.Multiply (le, re);
4482 case Operator.RightShift:
4483 return SLE.Expression.RightShift (le, re);
4484 case Operator.Subtraction:
4485 return is_checked ? SLE.Expression.SubtractChecked (le, re) : SLE.Expression.Subtract (le, re);
4486 default:
4487 throw new NotImplementedException (oper.ToString ());
4492 // D operator + (D x, D y)
4493 // D operator - (D x, D y)
4495 Expression ResolveOperatorDelegate (ResolveContext ec, TypeSpec l, TypeSpec r)
4497 if (l != r && !TypeSpecComparer.Variant.IsEqual (r, l)) {
4498 Expression tmp;
4499 if (right.eclass == ExprClass.MethodGroup || r == InternalType.AnonymousMethod || r == InternalType.NullLiteral) {
4500 tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
4501 if (tmp == null)
4502 return null;
4503 right = tmp;
4504 r = right.Type;
4505 } else if (left.eclass == ExprClass.MethodGroup || (l == InternalType.AnonymousMethod || l == InternalType.NullLiteral)) {
4506 tmp = Convert.ImplicitConversionRequired (ec, left, r, loc);
4507 if (tmp == null)
4508 return null;
4509 left = tmp;
4510 l = left.Type;
4511 } else {
4512 return null;
4516 MethodSpec method = null;
4517 Arguments args = new Arguments (2);
4518 args.Add (new Argument (left));
4519 args.Add (new Argument (right));
4521 if (oper == Operator.Addition) {
4522 method = ec.Module.PredefinedMembers.DelegateCombine.Resolve (loc);
4523 } else if (oper == Operator.Subtraction) {
4524 method = ec.Module.PredefinedMembers.DelegateRemove.Resolve (loc);
4527 if (method == null)
4528 return new EmptyExpression (ec.BuiltinTypes.Decimal);
4530 Expression expr = new UserOperatorCall (method, args, CreateExpressionTree, loc);
4531 return new ClassCast (expr, l);
4535 // Resolves enumeration operators where only single predefined overload exists, handles lifted versions too
4537 Expression ResolveSingleEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype)
4540 // bool operator == (E x, E y);
4541 // bool operator != (E x, E y);
4542 // bool operator < (E x, E y);
4543 // bool operator > (E x, E y);
4544 // bool operator <= (E x, E y);
4545 // bool operator >= (E x, E y);
4547 // E operator & (E x, E y);
4548 // E operator | (E x, E y);
4549 // E operator ^ (E x, E y);
4551 Expression expr;
4552 if ((oper & Operator.ComparisonMask) != 0) {
4553 type = rc.BuiltinTypes.Bool;
4554 } else {
4555 if (lenum)
4556 type = ltype;
4557 else if (renum)
4558 type = rtype;
4559 else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum)
4560 type = ltype;
4561 else
4562 type = rtype;
4565 if (ltype == rtype) {
4566 if (lenum || renum)
4567 return this;
4569 var lifted = new Nullable.LiftedBinaryOperator (this);
4570 lifted.Left = left;
4571 lifted.Right = right;
4572 return lifted.Resolve (rc);
4575 if (renum && !ltype.IsNullableType) {
4576 expr = Convert.ImplicitConversion (rc, left, rtype, loc);
4577 if (expr != null) {
4578 left = expr;
4579 return this;
4581 } else if (lenum && !rtype.IsNullableType) {
4582 expr = Convert.ImplicitConversion (rc, right, ltype, loc);
4583 if (expr != null) {
4584 right = expr;
4585 return this;
4590 // Now try lifted version of predefined operator
4592 var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec;
4593 if (nullable_type != null) {
4594 if (renum && !ltype.IsNullableType) {
4595 var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { rtype });
4597 expr = Convert.ImplicitConversion (rc, left, lifted_type, loc);
4598 if (expr != null) {
4599 left = expr;
4600 right = Convert.ImplicitConversion (rc, right, lifted_type, loc);
4603 if ((oper & Operator.BitwiseMask) != 0)
4604 type = lifted_type;
4606 if (left.IsNull) {
4607 if ((oper & Operator.BitwiseMask) != 0)
4608 return Nullable.LiftedNull.CreateFromExpression (rc, this);
4610 return CreateLiftedValueTypeResult (rc, rtype);
4613 if (expr != null) {
4614 var lifted = new Nullable.LiftedBinaryOperator (this);
4615 lifted.Left = expr;
4616 lifted.Right = right;
4617 return lifted.Resolve (rc);
4619 } else if (lenum && !rtype.IsNullableType) {
4620 var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { ltype });
4622 expr = Convert.ImplicitConversion (rc, right, lifted_type, loc);
4623 if (expr != null) {
4624 right = expr;
4625 left = Convert.ImplicitConversion (rc, left, lifted_type, loc);
4628 if ((oper & Operator.BitwiseMask) != 0)
4629 type = lifted_type;
4631 if (right.IsNull) {
4632 if ((oper & Operator.BitwiseMask) != 0)
4633 return Nullable.LiftedNull.CreateFromExpression (rc, this);
4635 return CreateLiftedValueTypeResult (rc, ltype);
4638 if (expr != null) {
4639 var lifted = new Nullable.LiftedBinaryOperator (this);
4640 lifted.Left = left;
4641 lifted.Right = expr;
4642 return lifted.Resolve (rc);
4644 } else if (rtype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (rtype).IsEnum) {
4645 Nullable.Unwrap unwrap = null;
4646 if (left.IsNull || right.IsNull) {
4647 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
4648 left = Convert.ImplicitConversion (rc, left, rtype, left.Location);
4650 if ((oper & Operator.RelationalMask) != 0)
4651 return CreateLiftedValueTypeResult (rc, rtype);
4653 if ((oper & Operator.BitwiseMask) != 0)
4654 return Nullable.LiftedNull.CreateFromExpression (rc, this);
4656 if (right.IsNull)
4657 return CreateLiftedValueTypeResult (rc, left.Type);
4659 // Equality operators are valid between E? and null
4660 expr = left;
4661 unwrap = new Nullable.Unwrap (right);
4662 } else {
4663 expr = Convert.ImplicitConversion (rc, left, Nullable.NullableInfo.GetUnderlyingType (rtype), loc);
4664 if (expr == null)
4665 return null;
4667 if ((oper & Operator.BitwiseMask) != 0)
4668 type = rtype;
4671 if (expr != null) {
4672 var lifted = new Nullable.LiftedBinaryOperator (this);
4673 lifted.Left = expr;
4674 lifted.Right = right;
4675 lifted.UnwrapRight = unwrap;
4676 return lifted.Resolve (rc);
4678 } else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum) {
4679 Nullable.Unwrap unwrap = null;
4680 if (right.IsNull || left.IsNull) {
4681 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
4682 right = Convert.ImplicitConversion (rc, right, ltype, right.Location);
4684 if ((oper & Operator.RelationalMask) != 0)
4685 return CreateLiftedValueTypeResult (rc, ltype);
4687 if ((oper & Operator.BitwiseMask) != 0)
4688 return Nullable.LiftedNull.CreateFromExpression (rc, this);
4690 if (left.IsNull)
4691 return CreateLiftedValueTypeResult (rc, right.Type);
4693 // Equality operators are valid between E? and null
4694 expr = right;
4695 unwrap = new Nullable.Unwrap (left);
4696 } else {
4697 expr = Convert.ImplicitConversion (rc, right, Nullable.NullableInfo.GetUnderlyingType (ltype), loc);
4698 if (expr == null)
4699 return null;
4701 if ((oper & Operator.BitwiseMask) != 0)
4702 type = ltype;
4705 if (expr != null) {
4706 var lifted = new Nullable.LiftedBinaryOperator (this);
4707 lifted.Left = left;
4708 lifted.UnwrapLeft = unwrap;
4709 lifted.Right = expr;
4710 return lifted.Resolve (rc);
4715 return null;
4718 static Expression ConvertEnumOperandToUnderlyingType (ResolveContext rc, Expression expr, bool liftType)
4720 TypeSpec underlying_type;
4721 if (expr.Type.IsNullableType) {
4722 var nt = Nullable.NullableInfo.GetUnderlyingType (expr.Type);
4723 if (nt.IsEnum)
4724 underlying_type = EnumSpec.GetUnderlyingType (nt);
4725 else
4726 underlying_type = nt;
4727 } else if (expr.Type.IsEnum) {
4728 underlying_type = EnumSpec.GetUnderlyingType (expr.Type);
4729 } else {
4730 underlying_type = expr.Type;
4733 switch (underlying_type.BuiltinType) {
4734 case BuiltinTypeSpec.Type.SByte:
4735 case BuiltinTypeSpec.Type.Byte:
4736 case BuiltinTypeSpec.Type.Short:
4737 case BuiltinTypeSpec.Type.UShort:
4738 underlying_type = rc.BuiltinTypes.Int;
4739 break;
4742 if (expr.Type.IsNullableType || liftType)
4743 underlying_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { underlying_type });
4745 if (expr.Type == underlying_type)
4746 return expr;
4748 return EmptyCast.Create (expr, underlying_type);
4751 Expression ResolveEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype)
4754 // U operator - (E e, E f)
4755 // E operator - (E e, U x) // Internal decomposition operator
4756 // E operator - (U x, E e) // Internal decomposition operator
4758 // E operator + (E e, U x)
4759 // E operator + (U x, E e)
4762 TypeSpec enum_type;
4764 if (lenum)
4765 enum_type = ltype;
4766 else if (renum)
4767 enum_type = rtype;
4768 else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum)
4769 enum_type = ltype;
4770 else
4771 enum_type = rtype;
4773 Expression expr;
4774 if (!enum_type.IsNullableType) {
4775 expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, false), false);
4776 if (expr != null) {
4777 if (oper == Operator.Subtraction)
4778 expr = ConvertEnumSubtractionResult (rc, expr);
4779 else
4780 expr = ConvertEnumAdditionalResult (expr, enum_type);
4782 enum_conversion = GetEnumResultCast (expr.Type);
4784 return expr;
4787 var nullable = rc.Module.PredefinedTypes.Nullable;
4790 // Don't try nullable version when nullable type is undefined
4792 if (!nullable.IsDefined)
4793 return null;
4795 enum_type = nullable.TypeSpec.MakeGenericType (rc.Module, new[] { enum_type });
4798 expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, true), false);
4799 if (expr != null) {
4800 if (oper == Operator.Subtraction)
4801 expr = ConvertEnumSubtractionResult (rc, expr);
4802 else
4803 expr = ConvertEnumAdditionalResult (expr, enum_type);
4805 enum_conversion = GetEnumResultCast (expr.Type);
4808 return expr;
4811 static Expression ConvertEnumAdditionalResult (Expression expr, TypeSpec enumType)
4813 return EmptyCast.Create (expr, enumType);
4816 Expression ConvertEnumSubtractionResult (ResolveContext rc, Expression expr)
4819 // Enumeration subtraction has different result type based on
4820 // best overload
4822 TypeSpec result_type;
4823 if (left.Type == right.Type) {
4824 var c = right as EnumConstant;
4825 if (c != null && c.IsZeroInteger && !right.Type.IsEnum) {
4827 // LAMESPEC: This is quite unexpected for expression E - 0 the return type is
4828 // E which is not what expressions E - 1 or 0 - E return
4830 result_type = left.Type;
4831 } else {
4832 result_type = left.Type.IsNullableType ?
4833 Nullable.NullableInfo.GetEnumUnderlyingType (rc.Module, left.Type) :
4834 EnumSpec.GetUnderlyingType (left.Type);
4836 } else {
4837 if (IsEnumOrNullableEnum (left.Type)) {
4838 result_type = left.Type;
4839 } else {
4840 result_type = right.Type;
4843 if (expr is Nullable.LiftedBinaryOperator && !result_type.IsNullableType)
4844 result_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { result_type });
4847 return EmptyCast.Create (expr, result_type);
4850 public static ConvCast.Mode GetEnumResultCast (TypeSpec type)
4852 if (type.IsNullableType)
4853 type = Nullable.NullableInfo.GetUnderlyingType (type);
4855 if (type.IsEnum)
4856 type = EnumSpec.GetUnderlyingType (type);
4858 switch (type.BuiltinType) {
4859 case BuiltinTypeSpec.Type.SByte:
4860 return ConvCast.Mode.I4_I1;
4861 case BuiltinTypeSpec.Type.Byte:
4862 return ConvCast.Mode.I4_U1;
4863 case BuiltinTypeSpec.Type.Short:
4864 return ConvCast.Mode.I4_I2;
4865 case BuiltinTypeSpec.Type.UShort:
4866 return ConvCast.Mode.I4_U2;
4869 return 0;
4873 // Equality operators rules
4875 Expression ResolveEquality (ResolveContext ec, TypeSpec l, TypeSpec r, bool primitives_only)
4877 Expression result;
4878 type = ec.BuiltinTypes.Bool;
4879 bool no_arg_conv = false;
4881 if (!primitives_only) {
4884 // a, Both operands are reference-type values or the value null
4885 // b, One operand is a value of type T where T is a type-parameter and
4886 // the other operand is the value null. Furthermore T does not have the
4887 // value type constraint
4889 // LAMESPEC: Very confusing details in the specification, basically any
4890 // reference like type-parameter is allowed
4892 var tparam_l = l as TypeParameterSpec;
4893 var tparam_r = r as TypeParameterSpec;
4894 if (tparam_l != null) {
4895 if (right is NullLiteral) {
4896 if (tparam_l.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType)
4897 return null;
4899 left = new BoxedCast (left, ec.BuiltinTypes.Object);
4900 return this;
4903 if (!tparam_l.IsReferenceType)
4904 return null;
4906 l = tparam_l.GetEffectiveBase ();
4907 left = new BoxedCast (left, l);
4908 } else if (left is NullLiteral && tparam_r == null) {
4909 if (TypeSpec.IsReferenceType (r))
4910 return this;
4912 if (r.Kind == MemberKind.InternalCompilerType)
4913 return null;
4916 if (tparam_r != null) {
4917 if (left is NullLiteral) {
4918 if (tparam_r.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType)
4919 return null;
4921 right = new BoxedCast (right, ec.BuiltinTypes.Object);
4922 return this;
4925 if (!tparam_r.IsReferenceType)
4926 return null;
4928 r = tparam_r.GetEffectiveBase ();
4929 right = new BoxedCast (right, r);
4930 } else if (right is NullLiteral) {
4931 if (TypeSpec.IsReferenceType (l))
4932 return this;
4934 if (l.Kind == MemberKind.InternalCompilerType)
4935 return null;
4939 // LAMESPEC: method groups can be compared when they convert to other side delegate
4941 if (l.IsDelegate) {
4942 if (right.eclass == ExprClass.MethodGroup) {
4943 result = Convert.ImplicitConversion (ec, right, l, loc);
4944 if (result == null)
4945 return null;
4947 right = result;
4948 r = l;
4949 } else if (r.IsDelegate && l != r) {
4950 return null;
4952 } else if (left.eclass == ExprClass.MethodGroup && r.IsDelegate) {
4953 result = Convert.ImplicitConversionRequired (ec, left, r, loc);
4954 if (result == null)
4955 return null;
4957 left = result;
4958 l = r;
4959 } else {
4960 no_arg_conv = l == r && !l.IsStruct;
4965 // bool operator != (string a, string b)
4966 // bool operator == (string a, string b)
4968 // bool operator != (Delegate a, Delegate b)
4969 // bool operator == (Delegate a, Delegate b)
4971 // bool operator != (bool a, bool b)
4972 // bool operator == (bool a, bool b)
4974 // LAMESPEC: Reference equality comparison can apply to value/reference types when
4975 // they implement an implicit conversion to any of types above. This does
4976 // not apply when both operands are of same reference type
4978 if (r.BuiltinType != BuiltinTypeSpec.Type.Object && l.BuiltinType != BuiltinTypeSpec.Type.Object) {
4979 result = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryEquality, no_arg_conv);
4980 if (result != null)
4981 return result;
4984 // Now try lifted version of predefined operators
4986 if (no_arg_conv && !l.IsNullableType) {
4988 // Optimizes cases which won't match
4990 } else {
4991 result = ResolveOperatorPredefined (ec, ec.Module.OperatorsBinaryEqualityLifted, no_arg_conv);
4992 if (result != null)
4993 return result;
4997 // The == and != operators permit one operand to be a value of a nullable
4998 // type and the other to be the null literal, even if no predefined or user-defined
4999 // operator (in unlifted or lifted form) exists for the operation.
5001 if ((l.IsNullableType && right.IsNull) || (r.IsNullableType && left.IsNull)) {
5002 var lifted = new Nullable.LiftedBinaryOperator (this);
5003 lifted.Left = left;
5004 lifted.Right = right;
5005 return lifted.Resolve (ec);
5010 // bool operator != (object a, object b)
5011 // bool operator == (object a, object b)
5013 // An explicit reference conversion exists from the
5014 // type of either operand to the type of the other operand.
5017 // Optimize common path
5018 if (l == r) {
5019 return l.Kind == MemberKind.InternalCompilerType || l.Kind == MemberKind.Struct ? null : this;
5022 if (!Convert.ExplicitReferenceConversionExists (l, r) &&
5023 !Convert.ExplicitReferenceConversionExists (r, l))
5024 return null;
5026 // Reject allowed explicit conversions like int->object
5027 if (!TypeSpec.IsReferenceType (l) || !TypeSpec.IsReferenceType (r))
5028 return null;
5030 if (l.BuiltinType == BuiltinTypeSpec.Type.String || l.BuiltinType == BuiltinTypeSpec.Type.Delegate || l.IsDelegate || MemberCache.GetUserOperator (l, CSharp.Operator.OpType.Equality, false) != null)
5031 ec.Report.Warning (253, 2, loc,
5032 "Possible unintended reference comparison. Consider casting the right side expression to type `{0}' to get value comparison",
5033 l.GetSignatureForError ());
5035 if (r.BuiltinType == BuiltinTypeSpec.Type.String || r.BuiltinType == BuiltinTypeSpec.Type.Delegate || r.IsDelegate || MemberCache.GetUserOperator (r, CSharp.Operator.OpType.Equality, false) != null)
5036 ec.Report.Warning (252, 2, loc,
5037 "Possible unintended reference comparison. Consider casting the left side expression to type `{0}' to get value comparison",
5038 r.GetSignatureForError ());
5040 return this;
5044 Expression ResolveOperatorPointer (ResolveContext ec, TypeSpec l, TypeSpec r)
5047 // bool operator == (void* x, void* y);
5048 // bool operator != (void* x, void* y);
5049 // bool operator < (void* x, void* y);
5050 // bool operator > (void* x, void* y);
5051 // bool operator <= (void* x, void* y);
5052 // bool operator >= (void* x, void* y);
5054 if ((oper & Operator.ComparisonMask) != 0) {
5055 Expression temp;
5056 if (!l.IsPointer) {
5057 temp = Convert.ImplicitConversion (ec, left, r, left.Location);
5058 if (temp == null)
5059 return null;
5060 left = temp;
5063 if (!r.IsPointer) {
5064 temp = Convert.ImplicitConversion (ec, right, l, right.Location);
5065 if (temp == null)
5066 return null;
5067 right = temp;
5070 type = ec.BuiltinTypes.Bool;
5071 return this;
5074 return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryUnsafe, false);
5078 // Build-in operators method overloading
5080 Expression ResolveOperatorPredefined (ResolveContext ec, PredefinedOperator [] operators, bool primitives_only)
5082 PredefinedOperator best_operator = null;
5083 TypeSpec l = left.Type;
5084 TypeSpec r = right.Type;
5085 Operator oper_mask = oper & ~Operator.ValuesOnlyMask;
5087 foreach (PredefinedOperator po in operators) {
5088 if ((po.OperatorsMask & oper_mask) == 0)
5089 continue;
5091 if (primitives_only) {
5092 if (!po.IsPrimitiveApplicable (l, r))
5093 continue;
5094 } else {
5095 if (!po.IsApplicable (ec, left, right))
5096 continue;
5099 if (best_operator == null) {
5100 best_operator = po;
5101 if (primitives_only)
5102 break;
5104 continue;
5107 best_operator = po.ResolveBetterOperator (ec, best_operator);
5109 if (best_operator == null) {
5110 ec.Report.Error (34, loc, "Operator `{0}' is ambiguous on operands of type `{1}' and `{2}'",
5111 OperName (oper), l.GetSignatureForError (), r.GetSignatureForError ());
5113 best_operator = po;
5114 break;
5118 if (best_operator == null)
5119 return null;
5121 return best_operator.ConvertResult (ec, this);
5125 // Optimize & constant expressions with 0 value
5127 Expression OptimizeAndOperation (Expression expr)
5129 Constant rc = right as Constant;
5130 Constant lc = left as Constant;
5131 if ((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue)) {
5133 // The result is a constant with side-effect
5135 Constant side_effect = rc == null ?
5136 new SideEffectConstant (lc, right, loc) :
5137 new SideEffectConstant (rc, left, loc);
5139 return ReducedExpression.Create (side_effect, expr);
5142 return expr;
5146 // Value types can be compared with the null literal because of the lifting
5147 // language rules. However the result is always true or false.
5149 public Expression CreateLiftedValueTypeResult (ResolveContext rc, TypeSpec valueType)
5151 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
5152 type = rc.BuiltinTypes.Bool;
5153 return this;
5156 // FIXME: Handle side effect constants
5157 Constant c = new BoolConstant (rc.BuiltinTypes, Oper == Operator.Inequality, loc);
5159 if ((Oper & Operator.EqualityMask) != 0) {
5160 rc.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is always `{1}'",
5161 valueType.GetSignatureForError (), c.GetValueAsLiteral ());
5162 } else {
5163 rc.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'",
5164 valueType.GetSignatureForError (), c.GetValueAsLiteral ());
5167 return c;
5171 // Performs user-operator overloading
5173 Expression ResolveUserOperator (ResolveContext rc, Expression left, Expression right)
5175 Expression oper_expr;
5177 var op = ConvertBinaryToUserOperator (oper);
5178 var l = left.Type;
5179 if (l.IsNullableType)
5180 l = Nullable.NullableInfo.GetUnderlyingType (l);
5181 var r = right.Type;
5182 if (r.IsNullableType)
5183 r = Nullable.NullableInfo.GetUnderlyingType (r);
5185 IList<MemberSpec> left_operators = MemberCache.GetUserOperator (l, op, false);
5186 IList<MemberSpec> right_operators = null;
5188 if (l != r) {
5189 right_operators = MemberCache.GetUserOperator (r, op, false);
5190 if (right_operators == null && left_operators == null)
5191 return null;
5192 } else if (left_operators == null) {
5193 return null;
5196 Arguments args = new Arguments (2);
5197 Argument larg = new Argument (left);
5198 args.Add (larg);
5199 Argument rarg = new Argument (right);
5200 args.Add (rarg);
5203 // User-defined operator implementations always take precedence
5204 // over predefined operator implementations
5206 if (left_operators != null && right_operators != null) {
5207 left_operators = CombineUserOperators (left_operators, right_operators);
5208 } else if (right_operators != null) {
5209 left_operators = right_operators;
5212 const OverloadResolver.Restrictions restr = OverloadResolver.Restrictions.ProbingOnly |
5213 OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded;
5215 var res = new OverloadResolver (left_operators, restr, loc);
5217 var oper_method = res.ResolveOperator (rc, ref args);
5218 if (oper_method == null) {
5220 // Logical && and || cannot be lifted
5222 if ((oper & Operator.LogicalMask) != 0)
5223 return null;
5226 // Apply lifted user operators only for liftable types. Implicit conversion
5227 // to nullable types is not allowed
5229 if (!IsLiftedOperatorApplicable ())
5230 return null;
5232 // TODO: Cache the result in module container
5233 var lifted_methods = CreateLiftedOperators (rc, left_operators);
5234 if (lifted_methods == null)
5235 return null;
5237 res = new OverloadResolver (lifted_methods, restr | OverloadResolver.Restrictions.ProbingOnly, loc);
5239 oper_method = res.ResolveOperator (rc, ref args);
5240 if (oper_method == null)
5241 return null;
5243 MethodSpec best_original = null;
5244 foreach (MethodSpec ms in left_operators) {
5245 if (ms.MemberDefinition == oper_method.MemberDefinition) {
5246 best_original = ms;
5247 break;
5251 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
5253 // Expression trees use lifted notation in this case
5255 this.left = Convert.ImplicitConversion (rc, left, oper_method.Parameters.Types[0], left.Location);
5256 this.right = Convert.ImplicitConversion (rc, right, oper_method.Parameters.Types[1], left.Location);
5259 var ptypes = best_original.Parameters.Types;
5261 if (left.IsNull || right.IsNull) {
5263 // The lifted operator produces a null value if one or both operands are null
5265 if ((oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0) {
5266 type = oper_method.ReturnType;
5267 return Nullable.LiftedNull.CreateFromExpression (rc, this);
5271 // The lifted operator produces the value false if one or both operands are null for
5272 // relational operators.
5274 if ((oper & Operator.RelationalMask) != 0) {
5276 // CSC BUG: This should be different warning, csc reports CS0458 with bool? which is wrong
5277 // because return type is actually bool
5279 return CreateLiftedValueTypeResult (rc, left.IsNull ? ptypes [1] : ptypes [0]);
5282 if ((oper & Operator.EqualityMask) != 0 && ((left.IsNull && !right.Type.IsNullableType) || !left.Type.IsNullableType)) {
5283 return CreateLiftedValueTypeResult (rc, left.IsNull ? ptypes [1] : ptypes [0]);
5287 type = oper_method.ReturnType;
5288 var lifted = new Nullable.LiftedBinaryOperator (this);
5289 lifted.UserOperator = best_original;
5291 if (left.Type.IsNullableType && !ptypes[0].IsNullableType) {
5292 lifted.UnwrapLeft = new Nullable.Unwrap (left);
5295 if (right.Type.IsNullableType && !ptypes[1].IsNullableType) {
5296 lifted.UnwrapRight = new Nullable.Unwrap (right);
5299 lifted.Left = Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? left, ptypes[0], left.Location);
5300 lifted.Right = Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? right, ptypes[1], right.Location);
5302 return lifted.Resolve (rc);
5305 if ((oper & Operator.LogicalMask) != 0) {
5306 // TODO: CreateExpressionTree is allocated every time
5307 oper_expr = new ConditionalLogicalOperator (oper_method, args, CreateExpressionTree,
5308 oper == Operator.LogicalAnd, loc).Resolve (rc);
5309 } else {
5310 oper_expr = new UserOperatorCall (oper_method, args, CreateExpressionTree, loc);
5313 this.left = larg.Expr;
5314 this.right = rarg.Expr;
5316 return oper_expr;
5319 bool IsLiftedOperatorApplicable ()
5321 if (left.Type.IsNullableType) {
5322 if ((oper & Operator.EqualityMask) != 0)
5323 return !right.IsNull;
5325 return true;
5328 if (right.Type.IsNullableType) {
5329 if ((oper & Operator.EqualityMask) != 0)
5330 return !left.IsNull;
5332 return true;
5335 if (TypeSpec.IsValueType (left.Type))
5336 return right.IsNull;
5338 if (TypeSpec.IsValueType (right.Type))
5339 return left.IsNull;
5341 return false;
5344 List<MemberSpec> CreateLiftedOperators (ResolveContext rc, IList<MemberSpec> operators)
5346 var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec;
5347 if (nullable_type == null)
5348 return null;
5351 // Lifted operators permit predefined and user-defined operators that operate
5352 // on non-nullable value types to also be used with nullable forms of those types.
5353 // Lifted operators are constructed from predefined and user-defined operators
5354 // that meet certain requirements
5356 List<MemberSpec> lifted = null;
5357 foreach (MethodSpec oper in operators) {
5358 TypeSpec rt;
5359 if ((Oper & Operator.ComparisonMask) != 0) {
5361 // Result type must be of type bool for lifted comparison operators
5363 rt = oper.ReturnType;
5364 if (rt.BuiltinType != BuiltinTypeSpec.Type.Bool)
5365 continue;
5366 } else {
5367 if (!TypeSpec.IsNonNullableValueType (oper.ReturnType))
5368 continue;
5370 rt = null;
5373 var ptypes = oper.Parameters.Types;
5374 if (!TypeSpec.IsNonNullableValueType (ptypes [0]) || !TypeSpec.IsNonNullableValueType (ptypes [1]))
5375 continue;
5378 // LAMESPEC: I am not sure why but for equality operators to be lifted
5379 // both types have to match
5381 if ((Oper & Operator.EqualityMask) != 0 && ptypes [0] != ptypes [1])
5382 continue;
5384 if (lifted == null)
5385 lifted = new List<MemberSpec> ();
5388 // The lifted form is constructed by adding a single ? modifier to each operand and
5389 // result type except for comparison operators where return type is bool
5391 if (rt == null)
5392 rt = nullable_type.MakeGenericType (rc.Module, new[] { oper.ReturnType });
5394 var parameters = ParametersCompiled.CreateFullyResolved (
5395 nullable_type.MakeGenericType (rc.Module, new [] { ptypes[0] }),
5396 nullable_type.MakeGenericType (rc.Module, new [] { ptypes[1] }));
5398 var lifted_op = new MethodSpec (oper.Kind, oper.DeclaringType, oper.MemberDefinition,
5399 rt, parameters, oper.Modifiers);
5401 lifted.Add (lifted_op);
5404 return lifted;
5408 // Merge two sets of user operators into one, they are mostly distinguish
5409 // except when they share base type and it contains an operator
5411 static IList<MemberSpec> CombineUserOperators (IList<MemberSpec> left, IList<MemberSpec> right)
5413 var combined = new List<MemberSpec> (left.Count + right.Count);
5414 combined.AddRange (left);
5415 foreach (var r in right) {
5416 bool same = false;
5417 foreach (var l in left) {
5418 if (l.DeclaringType == r.DeclaringType) {
5419 same = true;
5420 break;
5424 if (!same)
5425 combined.Add (r);
5428 return combined;
5431 void CheckOutOfRangeComparison (ResolveContext ec, Constant c, TypeSpec type)
5433 if (c is IntegralConstant || c is CharConstant) {
5434 try {
5435 c.ConvertExplicitly (true, type);
5436 } catch (OverflowException) {
5437 ec.Report.Warning (652, 2, loc,
5438 "A comparison between a constant and a variable is useless. The constant is out of the range of the variable type `{0}'",
5439 type.GetSignatureForError ());
5444 /// <remarks>
5445 /// EmitBranchable is called from Statement.EmitBoolExpression in the
5446 /// context of a conditional bool expression. This function will return
5447 /// false if it is was possible to use EmitBranchable, or true if it was.
5449 /// The expression's code is generated, and we will generate a branch to `target'
5450 /// if the resulting expression value is equal to isTrue
5451 /// </remarks>
5452 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
5454 if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) {
5455 left = left.EmitToField (ec);
5457 if ((oper & Operator.LogicalMask) == 0) {
5458 right = right.EmitToField (ec);
5463 // This is more complicated than it looks, but its just to avoid
5464 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
5465 // but on top of that we want for == and != to use a special path
5466 // if we are comparing against null
5468 if ((oper & Operator.EqualityMask) != 0 && (left is Constant || right is Constant)) {
5469 bool my_on_true = oper == Operator.Inequality ? on_true : !on_true;
5472 // put the constant on the rhs, for simplicity
5474 if (left is Constant) {
5475 Expression swap = right;
5476 right = left;
5477 left = swap;
5481 // brtrue/brfalse works with native int only
5483 if (((Constant) right).IsZeroInteger && right.Type.BuiltinType != BuiltinTypeSpec.Type.Long && right.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5484 left.EmitBranchable (ec, target, my_on_true);
5485 return;
5487 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) {
5488 // right is a boolean, and it's not 'false' => it is 'true'
5489 left.EmitBranchable (ec, target, !my_on_true);
5490 return;
5493 } else if (oper == Operator.LogicalAnd) {
5495 if (on_true) {
5496 Label tests_end = ec.DefineLabel ();
5498 left.EmitBranchable (ec, tests_end, false);
5499 right.EmitBranchable (ec, target, true);
5500 ec.MarkLabel (tests_end);
5501 } else {
5503 // This optimizes code like this
5504 // if (true && i > 4)
5506 if (!(left is Constant))
5507 left.EmitBranchable (ec, target, false);
5509 if (!(right is Constant))
5510 right.EmitBranchable (ec, target, false);
5513 return;
5515 } else if (oper == Operator.LogicalOr){
5516 if (on_true) {
5517 left.EmitBranchable (ec, target, true);
5518 right.EmitBranchable (ec, target, true);
5520 } else {
5521 Label tests_end = ec.DefineLabel ();
5522 left.EmitBranchable (ec, tests_end, true);
5523 right.EmitBranchable (ec, target, false);
5524 ec.MarkLabel (tests_end);
5527 return;
5529 } else if ((oper & Operator.ComparisonMask) == 0) {
5530 base.EmitBranchable (ec, target, on_true);
5531 return;
5534 left.Emit (ec);
5535 right.Emit (ec);
5537 TypeSpec t = left.Type;
5538 bool is_float = IsFloat (t);
5539 bool is_unsigned = is_float || IsUnsigned (t);
5541 switch (oper){
5542 case Operator.Equality:
5543 if (on_true)
5544 ec.Emit (OpCodes.Beq, target);
5545 else
5546 ec.Emit (OpCodes.Bne_Un, target);
5547 break;
5549 case Operator.Inequality:
5550 if (on_true)
5551 ec.Emit (OpCodes.Bne_Un, target);
5552 else
5553 ec.Emit (OpCodes.Beq, target);
5554 break;
5556 case Operator.LessThan:
5557 if (on_true)
5558 if (is_unsigned && !is_float)
5559 ec.Emit (OpCodes.Blt_Un, target);
5560 else
5561 ec.Emit (OpCodes.Blt, target);
5562 else
5563 if (is_unsigned)
5564 ec.Emit (OpCodes.Bge_Un, target);
5565 else
5566 ec.Emit (OpCodes.Bge, target);
5567 break;
5569 case Operator.GreaterThan:
5570 if (on_true)
5571 if (is_unsigned && !is_float)
5572 ec.Emit (OpCodes.Bgt_Un, target);
5573 else
5574 ec.Emit (OpCodes.Bgt, target);
5575 else
5576 if (is_unsigned)
5577 ec.Emit (OpCodes.Ble_Un, target);
5578 else
5579 ec.Emit (OpCodes.Ble, target);
5580 break;
5582 case Operator.LessThanOrEqual:
5583 if (on_true)
5584 if (is_unsigned && !is_float)
5585 ec.Emit (OpCodes.Ble_Un, target);
5586 else
5587 ec.Emit (OpCodes.Ble, target);
5588 else
5589 if (is_unsigned)
5590 ec.Emit (OpCodes.Bgt_Un, target);
5591 else
5592 ec.Emit (OpCodes.Bgt, target);
5593 break;
5596 case Operator.GreaterThanOrEqual:
5597 if (on_true)
5598 if (is_unsigned && !is_float)
5599 ec.Emit (OpCodes.Bge_Un, target);
5600 else
5601 ec.Emit (OpCodes.Bge, target);
5602 else
5603 if (is_unsigned)
5604 ec.Emit (OpCodes.Blt_Un, target);
5605 else
5606 ec.Emit (OpCodes.Blt, target);
5607 break;
5608 default:
5609 throw new InternalErrorException (oper.ToString ());
5613 public override void Emit (EmitContext ec)
5615 if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) {
5616 left = left.EmitToField (ec);
5618 if ((oper & Operator.LogicalMask) == 0) {
5619 right = right.EmitToField (ec);
5624 // Handle short-circuit operators differently
5625 // than the rest
5627 if ((oper & Operator.LogicalMask) != 0) {
5628 Label load_result = ec.DefineLabel ();
5629 Label end = ec.DefineLabel ();
5631 bool is_or = oper == Operator.LogicalOr;
5632 left.EmitBranchable (ec, load_result, is_or);
5633 right.Emit (ec);
5634 ec.Emit (OpCodes.Br_S, end);
5636 ec.MarkLabel (load_result);
5637 ec.EmitInt (is_or ? 1 : 0);
5638 ec.MarkLabel (end);
5639 return;
5643 // Optimize zero-based operations which cannot be optimized at expression level
5645 if (oper == Operator.Subtraction) {
5646 var lc = left as IntegralConstant;
5647 if (lc != null && lc.IsDefaultValue) {
5648 right.Emit (ec);
5649 ec.Emit (OpCodes.Neg);
5650 return;
5654 EmitOperator (ec, left, right);
5657 public void EmitOperator (EmitContext ec, Expression left, Expression right)
5659 left.Emit (ec);
5660 right.Emit (ec);
5662 EmitOperatorOpcode (ec, oper, left.Type, right);
5665 // Emit result enumerable conversion this way because it's quite complicated get it
5666 // to resolved tree because expression tree cannot see it.
5668 if (enum_conversion != 0)
5669 ConvCast.Emit (ec, enum_conversion);
5672 public override void EmitSideEffect (EmitContext ec)
5674 if ((oper & Operator.LogicalMask) != 0 ||
5675 (ec.HasSet (EmitContext.Options.CheckedScope) && (oper == Operator.Multiply || oper == Operator.Addition || oper == Operator.Subtraction))) {
5676 base.EmitSideEffect (ec);
5677 } else {
5678 left.EmitSideEffect (ec);
5679 right.EmitSideEffect (ec);
5683 public override Expression EmitToField (EmitContext ec)
5685 if ((oper & Operator.LogicalMask) == 0) {
5686 var await_expr = left as Await;
5687 if (await_expr != null && right.IsSideEffectFree) {
5688 await_expr.Statement.EmitPrologue (ec);
5689 left = await_expr.Statement.GetResultExpression (ec);
5690 return this;
5693 await_expr = right as Await;
5694 if (await_expr != null && left.IsSideEffectFree) {
5695 await_expr.Statement.EmitPrologue (ec);
5696 right = await_expr.Statement.GetResultExpression (ec);
5697 return this;
5701 return base.EmitToField (ec);
5704 protected override void CloneTo (CloneContext clonectx, Expression t)
5706 Binary target = (Binary) t;
5708 target.left = left.Clone (clonectx);
5709 target.right = right.Clone (clonectx);
5712 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
5714 Arguments binder_args = new Arguments (4);
5716 MemberAccess sle = new MemberAccess (new MemberAccess (
5717 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc);
5719 CSharpBinderFlags flags = 0;
5720 if (ec.HasSet (ResolveContext.Options.CheckedScope))
5721 flags = CSharpBinderFlags.CheckedContext;
5723 if ((oper & Operator.LogicalMask) != 0)
5724 flags |= CSharpBinderFlags.BinaryOperationLogical;
5726 binder_args.Add (new Argument (new EnumConstant (new IntLiteral (ec.BuiltinTypes, (int) flags, loc), ec.Module.PredefinedTypes.BinderFlags.Resolve ())));
5727 binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), GetOperatorExpressionTypeName (), loc)));
5728 binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc)));
5729 binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
5731 return new Invocation (new MemberAccess (new TypeExpression (ec.Module.PredefinedTypes.Binder.TypeSpec, loc), "BinaryOperation", loc), binder_args);
5734 public override Expression CreateExpressionTree (ResolveContext ec)
5736 return CreateExpressionTree (ec, null);
5739 public Expression CreateExpressionTree (ResolveContext ec, Expression method)
5741 string method_name;
5742 bool lift_arg = false;
5744 switch (oper) {
5745 case Operator.Addition:
5746 if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type))
5747 method_name = "AddChecked";
5748 else
5749 method_name = "Add";
5750 break;
5751 case Operator.BitwiseAnd:
5752 method_name = "And";
5753 break;
5754 case Operator.BitwiseOr:
5755 method_name = "Or";
5756 break;
5757 case Operator.Division:
5758 method_name = "Divide";
5759 break;
5760 case Operator.Equality:
5761 method_name = "Equal";
5762 lift_arg = true;
5763 break;
5764 case Operator.ExclusiveOr:
5765 method_name = "ExclusiveOr";
5766 break;
5767 case Operator.GreaterThan:
5768 method_name = "GreaterThan";
5769 lift_arg = true;
5770 break;
5771 case Operator.GreaterThanOrEqual:
5772 method_name = "GreaterThanOrEqual";
5773 lift_arg = true;
5774 break;
5775 case Operator.Inequality:
5776 method_name = "NotEqual";
5777 lift_arg = true;
5778 break;
5779 case Operator.LeftShift:
5780 method_name = "LeftShift";
5781 break;
5782 case Operator.LessThan:
5783 method_name = "LessThan";
5784 lift_arg = true;
5785 break;
5786 case Operator.LessThanOrEqual:
5787 method_name = "LessThanOrEqual";
5788 lift_arg = true;
5789 break;
5790 case Operator.LogicalAnd:
5791 method_name = "AndAlso";
5792 break;
5793 case Operator.LogicalOr:
5794 method_name = "OrElse";
5795 break;
5796 case Operator.Modulus:
5797 method_name = "Modulo";
5798 break;
5799 case Operator.Multiply:
5800 if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type))
5801 method_name = "MultiplyChecked";
5802 else
5803 method_name = "Multiply";
5804 break;
5805 case Operator.RightShift:
5806 method_name = "RightShift";
5807 break;
5808 case Operator.Subtraction:
5809 if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type))
5810 method_name = "SubtractChecked";
5811 else
5812 method_name = "Subtract";
5813 break;
5815 default:
5816 throw new InternalErrorException ("Unknown expression tree binary operator " + oper);
5819 Arguments args = new Arguments (2);
5820 args.Add (new Argument (left.CreateExpressionTree (ec)));
5821 args.Add (new Argument (right.CreateExpressionTree (ec)));
5822 if (method != null) {
5823 if (lift_arg)
5824 args.Add (new Argument (new BoolLiteral (ec.BuiltinTypes, false, loc)));
5826 args.Add (new Argument (method));
5829 return CreateExpressionFactoryCall (ec, method_name, args);
5832 public override object Accept (StructuralVisitor visitor)
5834 return visitor.Visit (this);
5840 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
5841 // b, c, d... may be strings or objects.
5843 public class StringConcat : Expression
5845 Arguments arguments;
5847 StringConcat (Location loc)
5849 this.loc = loc;
5850 arguments = new Arguments (2);
5853 public override bool ContainsEmitWithAwait ()
5855 return arguments.ContainsEmitWithAwait ();
5858 public static StringConcat Create (ResolveContext rc, Expression left, Expression right, Location loc)
5860 if (left.eclass == ExprClass.Unresolved || right.eclass == ExprClass.Unresolved)
5861 throw new ArgumentException ();
5863 var s = new StringConcat (loc);
5864 s.type = rc.BuiltinTypes.String;
5865 s.eclass = ExprClass.Value;
5867 s.Append (rc, left);
5868 s.Append (rc, right);
5869 return s;
5872 public override Expression CreateExpressionTree (ResolveContext ec)
5874 Argument arg = arguments [0];
5875 return CreateExpressionAddCall (ec, arg, arg.CreateExpressionTree (ec), 1);
5879 // Creates nested calls tree from an array of arguments used for IL emit
5881 Expression CreateExpressionAddCall (ResolveContext ec, Argument left, Expression left_etree, int pos)
5883 Arguments concat_args = new Arguments (2);
5884 Arguments add_args = new Arguments (3);
5886 concat_args.Add (left);
5887 add_args.Add (new Argument (left_etree));
5889 concat_args.Add (arguments [pos]);
5890 add_args.Add (new Argument (arguments [pos].CreateExpressionTree (ec)));
5892 var methods = GetConcatMethodCandidates ();
5893 if (methods == null)
5894 return null;
5896 var res = new OverloadResolver (methods, OverloadResolver.Restrictions.NoBaseMembers, loc);
5897 var method = res.ResolveMember<MethodSpec> (ec, ref concat_args);
5898 if (method == null)
5899 return null;
5901 add_args.Add (new Argument (new TypeOfMethod (method, loc)));
5903 Expression expr = CreateExpressionFactoryCall (ec, "Add", add_args);
5904 if (++pos == arguments.Count)
5905 return expr;
5907 left = new Argument (new EmptyExpression (method.ReturnType));
5908 return CreateExpressionAddCall (ec, left, expr, pos);
5911 protected override Expression DoResolve (ResolveContext ec)
5913 return this;
5916 void Append (ResolveContext rc, Expression operand)
5919 // Constant folding
5921 StringConstant sc = operand as StringConstant;
5922 if (sc != null) {
5923 if (arguments.Count != 0) {
5924 Argument last_argument = arguments [arguments.Count - 1];
5925 StringConstant last_expr_constant = last_argument.Expr as StringConstant;
5926 if (last_expr_constant != null) {
5927 last_argument.Expr = new StringConstant (rc.BuiltinTypes, last_expr_constant.Value + sc.Value, sc.Location);
5928 return;
5931 } else {
5933 // Multiple (3+) concatenation are resolved as multiple StringConcat instances
5935 StringConcat concat_oper = operand as StringConcat;
5936 if (concat_oper != null) {
5937 arguments.AddRange (concat_oper.arguments);
5938 return;
5942 arguments.Add (new Argument (operand));
5945 IList<MemberSpec> GetConcatMethodCandidates ()
5947 return MemberCache.FindMembers (type, "Concat", true);
5950 public override void Emit (EmitContext ec)
5952 // Optimize by removing any extra null arguments, they are no-op
5953 for (int i = 0; i < arguments.Count; ++i) {
5954 if (arguments[i].Expr is NullConstant)
5955 arguments.RemoveAt (i--);
5958 var members = GetConcatMethodCandidates ();
5959 var res = new OverloadResolver (members, OverloadResolver.Restrictions.NoBaseMembers, loc);
5960 var method = res.ResolveMember<MethodSpec> (new ResolveContext (ec.MemberContext), ref arguments);
5961 if (method != null) {
5962 var call = new CallEmitter ();
5963 call.EmitPredefined (ec, method, arguments, false);
5967 public override void FlowAnalysis (FlowAnalysisContext fc)
5969 arguments.FlowAnalysis (fc);
5972 public override SLE.Expression MakeExpression (BuilderContext ctx)
5974 if (arguments.Count != 2)
5975 throw new NotImplementedException ("arguments.Count != 2");
5977 var concat = typeof (string).GetMethod ("Concat", new[] { typeof (object), typeof (object) });
5978 return SLE.Expression.Add (arguments[0].Expr.MakeExpression (ctx), arguments[1].Expr.MakeExpression (ctx), concat);
5983 // User-defined conditional logical operator
5985 public class ConditionalLogicalOperator : UserOperatorCall
5987 readonly bool is_and;
5988 Expression oper_expr;
5990 public ConditionalLogicalOperator (MethodSpec oper, Arguments arguments, Func<ResolveContext, Expression, Expression> expr_tree, bool is_and, Location loc)
5991 : base (oper, arguments, expr_tree, loc)
5993 this.is_and = is_and;
5994 eclass = ExprClass.Unresolved;
5997 protected override Expression DoResolve (ResolveContext ec)
5999 AParametersCollection pd = oper.Parameters;
6000 if (!TypeSpecComparer.IsEqual (type, pd.Types[0]) || !TypeSpecComparer.IsEqual (type, pd.Types[1])) {
6001 ec.Report.Error (217, loc,
6002 "A user-defined operator `{0}' must have each parameter type and return type of the same type in order to be applicable as a short circuit operator",
6003 oper.GetSignatureForError ());
6004 return null;
6007 Expression left_dup = new EmptyExpression (type);
6008 Expression op_true = GetOperatorTrue (ec, left_dup, loc);
6009 Expression op_false = GetOperatorFalse (ec, left_dup, loc);
6010 if (op_true == null || op_false == null) {
6011 ec.Report.Error (218, loc,
6012 "The type `{0}' must have operator `true' and operator `false' defined when `{1}' is used as a short circuit operator",
6013 type.GetSignatureForError (), oper.GetSignatureForError ());
6014 return null;
6017 oper_expr = is_and ? op_false : op_true;
6018 eclass = ExprClass.Value;
6019 return this;
6022 public override void Emit (EmitContext ec)
6024 Label end_target = ec.DefineLabel ();
6027 // Emit and duplicate left argument
6029 bool right_contains_await = ec.HasSet (BuilderContext.Options.AsyncBody) && arguments[1].Expr.ContainsEmitWithAwait ();
6030 if (right_contains_await) {
6031 arguments[0] = arguments[0].EmitToField (ec, false);
6032 arguments[0].Expr.Emit (ec);
6033 } else {
6034 arguments[0].Expr.Emit (ec);
6035 ec.Emit (OpCodes.Dup);
6036 arguments.RemoveAt (0);
6039 oper_expr.EmitBranchable (ec, end_target, true);
6041 base.Emit (ec);
6043 if (right_contains_await) {
6045 // Special handling when right expression contains await and left argument
6046 // could not be left on stack before logical branch
6048 Label skip_left_load = ec.DefineLabel ();
6049 ec.Emit (OpCodes.Br_S, skip_left_load);
6050 ec.MarkLabel (end_target);
6051 arguments[0].Expr.Emit (ec);
6052 ec.MarkLabel (skip_left_load);
6053 } else {
6054 ec.MarkLabel (end_target);
6059 public class PointerArithmetic : Expression {
6060 Expression left, right;
6061 readonly Binary.Operator op;
6064 // We assume that `l' is always a pointer
6066 public PointerArithmetic (Binary.Operator op, Expression l, Expression r, TypeSpec t, Location loc)
6068 type = t;
6069 this.loc = loc;
6070 left = l;
6071 right = r;
6072 this.op = op;
6075 public override bool ContainsEmitWithAwait ()
6077 throw new NotImplementedException ();
6080 public override Expression CreateExpressionTree (ResolveContext ec)
6082 Error_PointerInsideExpressionTree (ec);
6083 return null;
6086 protected override Expression DoResolve (ResolveContext ec)
6088 eclass = ExprClass.Variable;
6090 var pc = left.Type as PointerContainer;
6091 if (pc != null && pc.Element.Kind == MemberKind.Void) {
6092 Error_VoidPointerOperation (ec);
6093 return null;
6096 return this;
6099 public override void Emit (EmitContext ec)
6101 TypeSpec op_type = left.Type;
6103 // It must be either array or fixed buffer
6104 TypeSpec element;
6105 if (TypeManager.HasElementType (op_type)) {
6106 element = TypeManager.GetElementType (op_type);
6107 } else {
6108 FieldExpr fe = left as FieldExpr;
6109 if (fe != null)
6110 element = ((FixedFieldSpec) (fe.Spec)).ElementType;
6111 else
6112 element = op_type;
6115 int size = BuiltinTypeSpec.GetSize(element);
6116 TypeSpec rtype = right.Type;
6118 if ((op & Binary.Operator.SubtractionMask) != 0 && rtype.IsPointer){
6120 // handle (pointer - pointer)
6122 left.Emit (ec);
6123 right.Emit (ec);
6124 ec.Emit (OpCodes.Sub);
6126 if (size != 1){
6127 if (size == 0)
6128 ec.Emit (OpCodes.Sizeof, element);
6129 else
6130 ec.EmitInt (size);
6131 ec.Emit (OpCodes.Div);
6133 ec.Emit (OpCodes.Conv_I8);
6134 } else {
6136 // handle + and - on (pointer op int)
6138 Constant left_const = left as Constant;
6139 if (left_const != null) {
6141 // Optimize ((T*)null) pointer operations
6143 if (left_const.IsDefaultValue) {
6144 left = EmptyExpression.Null;
6145 } else {
6146 left_const = null;
6150 left.Emit (ec);
6152 var right_const = right as Constant;
6153 if (right_const != null) {
6155 // Optimize 0-based arithmetic
6157 if (right_const.IsDefaultValue)
6158 return;
6160 if (size != 0)
6161 right = new IntConstant (ec.BuiltinTypes, size, right.Location);
6162 else
6163 right = new SizeOf (new TypeExpression (element, right.Location), right.Location);
6165 // TODO: Should be the checks resolve context sensitive?
6166 ResolveContext rc = new ResolveContext (ec.MemberContext, ResolveContext.Options.UnsafeScope);
6167 right = new Binary (Binary.Operator.Multiply, right, right_const).Resolve (rc);
6168 if (right == null)
6169 return;
6172 right.Emit (ec);
6173 if (right_const == null) {
6174 switch (rtype.BuiltinType) {
6175 case BuiltinTypeSpec.Type.SByte:
6176 case BuiltinTypeSpec.Type.Byte:
6177 case BuiltinTypeSpec.Type.Short:
6178 case BuiltinTypeSpec.Type.UShort:
6179 case BuiltinTypeSpec.Type.Int:
6180 ec.Emit (OpCodes.Conv_I);
6181 break;
6182 case BuiltinTypeSpec.Type.UInt:
6183 ec.Emit (OpCodes.Conv_U);
6184 break;
6188 if (right_const == null && size != 1){
6189 if (size == 0)
6190 ec.Emit (OpCodes.Sizeof, element);
6191 else
6192 ec.EmitInt (size);
6193 if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long || rtype.BuiltinType == BuiltinTypeSpec.Type.ULong)
6194 ec.Emit (OpCodes.Conv_I8);
6196 Binary.EmitOperatorOpcode (ec, Binary.Operator.Multiply, rtype, right);
6199 if (left_const == null) {
6200 if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long)
6201 ec.Emit (OpCodes.Conv_I);
6202 else if (rtype.BuiltinType == BuiltinTypeSpec.Type.ULong)
6203 ec.Emit (OpCodes.Conv_U);
6205 Binary.EmitOperatorOpcode (ec, op, op_type, right);
6212 // A boolean-expression is an expression that yields a result
6213 // of type bool
6215 public class BooleanExpression : ShimExpression
6217 public BooleanExpression (Expression expr)
6218 : base (expr)
6220 this.loc = expr.Location;
6223 public override Expression CreateExpressionTree (ResolveContext ec)
6225 // TODO: We should emit IsTrue (v4) instead of direct user operator
6226 // call but that would break csc compatibility
6227 return base.CreateExpressionTree (ec);
6230 protected override Expression DoResolve (ResolveContext ec)
6232 // A boolean-expression is required to be of a type
6233 // that can be implicitly converted to bool or of
6234 // a type that implements operator true
6236 expr = expr.Resolve (ec);
6237 if (expr == null)
6238 return null;
6240 Assign ass = expr as Assign;
6241 if (ass != null && ass.Source is Constant) {
6242 ec.Report.Warning (665, 3, loc,
6243 "Assignment in conditional expression is always constant. Did you mean to use `==' instead ?");
6246 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Bool)
6247 return expr;
6249 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
6250 Arguments args = new Arguments (1);
6251 args.Add (new Argument (expr));
6252 return DynamicUnaryConversion.CreateIsTrue (ec, args, loc).Resolve (ec);
6255 type = ec.BuiltinTypes.Bool;
6256 Expression converted = Convert.ImplicitConversion (ec, expr, type, loc);
6257 if (converted != null)
6258 return converted;
6261 // If no implicit conversion to bool exists, try using `operator true'
6263 converted = GetOperatorTrue (ec, expr, loc);
6264 if (converted == null) {
6265 expr.Error_ValueCannotBeConverted (ec, type, false);
6266 return null;
6269 return converted;
6272 public override object Accept (StructuralVisitor visitor)
6274 return visitor.Visit (this);
6278 public class BooleanExpressionFalse : Unary
6280 public BooleanExpressionFalse (Expression expr)
6281 : base (Operator.LogicalNot, expr, expr.Location)
6285 protected override Expression ResolveOperator (ResolveContext ec, Expression expr)
6287 return GetOperatorFalse (ec, expr, loc) ?? base.ResolveOperator (ec, expr);
6291 /// <summary>
6292 /// Implements the ternary conditional operator (?:)
6293 /// </summary>
6294 public class Conditional : Expression {
6295 Expression expr, true_expr, false_expr;
6297 public Conditional (Expression expr, Expression true_expr, Expression false_expr, Location loc)
6299 this.expr = expr;
6300 this.true_expr = true_expr;
6301 this.false_expr = false_expr;
6302 this.loc = loc;
6305 #region Properties
6307 public Expression Expr {
6308 get {
6309 return expr;
6313 public Expression TrueExpr {
6314 get {
6315 return true_expr;
6319 public Expression FalseExpr {
6320 get {
6321 return false_expr;
6325 #endregion
6327 public override bool ContainsEmitWithAwait ()
6329 return Expr.ContainsEmitWithAwait () || true_expr.ContainsEmitWithAwait () || false_expr.ContainsEmitWithAwait ();
6332 public override Expression CreateExpressionTree (ResolveContext ec)
6334 Arguments args = new Arguments (3);
6335 args.Add (new Argument (expr.CreateExpressionTree (ec)));
6336 args.Add (new Argument (true_expr.CreateExpressionTree (ec)));
6337 args.Add (new Argument (false_expr.CreateExpressionTree (ec)));
6338 return CreateExpressionFactoryCall (ec, "Condition", args);
6341 protected override Expression DoResolve (ResolveContext ec)
6343 expr = expr.Resolve (ec);
6344 true_expr = true_expr.Resolve (ec);
6345 false_expr = false_expr.Resolve (ec);
6347 if (true_expr == null || false_expr == null || expr == null)
6348 return null;
6350 eclass = ExprClass.Value;
6351 TypeSpec true_type = true_expr.Type;
6352 TypeSpec false_type = false_expr.Type;
6353 type = true_type;
6356 // First, if an implicit conversion exists from true_expr
6357 // to false_expr, then the result type is of type false_expr.Type
6359 if (!TypeSpecComparer.IsEqual (true_type, false_type)) {
6360 Expression conv = Convert.ImplicitConversion (ec, true_expr, false_type, loc);
6361 if (conv != null && true_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) {
6363 // Check if both can convert implicitly to each other's type
6365 type = false_type;
6367 if (false_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) {
6368 var conv_false_expr = Convert.ImplicitConversion (ec, false_expr, true_type, loc);
6370 // LAMESPEC: There seems to be hardcoded promotition to int type when
6371 // both sides are numeric constants and one side is int constant and
6372 // other side is numeric constant convertible to int.
6374 // var res = condition ? (short)1 : 1;
6376 // Type of res is int even if according to the spec the conversion is
6377 // ambiguous because 1 literal can be converted to short.
6379 if (conv_false_expr != null) {
6380 if (conv_false_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Int && conv is Constant) {
6381 type = true_type;
6382 conv_false_expr = null;
6383 } else if (type.BuiltinType == BuiltinTypeSpec.Type.Int && conv_false_expr is Constant) {
6384 conv_false_expr = null;
6388 if (conv_false_expr != null) {
6389 ec.Report.Error (172, true_expr.Location,
6390 "Type of conditional expression cannot be determined as `{0}' and `{1}' convert implicitly to each other",
6391 true_type.GetSignatureForError (), false_type.GetSignatureForError ());
6395 true_expr = conv;
6396 if (true_expr.Type != type)
6397 true_expr = EmptyCast.Create (true_expr, type);
6398 } else if ((conv = Convert.ImplicitConversion (ec, false_expr, true_type, loc)) != null) {
6399 false_expr = conv;
6400 } else {
6401 if (false_type != InternalType.ErrorType) {
6402 ec.Report.Error (173, true_expr.Location,
6403 "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
6404 true_type.GetSignatureForError (), false_type.GetSignatureForError ());
6406 return null;
6410 Constant c = expr as Constant;
6411 if (c != null) {
6412 bool is_false = c.IsDefaultValue;
6415 // Don't issue the warning for constant expressions
6417 if (!(is_false ? true_expr is Constant : false_expr is Constant)) {
6418 // CSC: Missing warning
6419 Warning_UnreachableExpression (ec, is_false ? true_expr.Location : false_expr.Location);
6422 return ReducedExpression.Create (
6423 is_false ? false_expr : true_expr, this,
6424 false_expr is Constant && true_expr is Constant).Resolve (ec);
6427 return this;
6430 public override void Emit (EmitContext ec)
6432 Label false_target = ec.DefineLabel ();
6433 Label end_target = ec.DefineLabel ();
6435 expr.EmitBranchable (ec, false_target, false);
6436 true_expr.Emit (ec);
6439 // Verifier doesn't support interface merging. When there are two types on
6440 // the stack without common type hint and the common type is an interface.
6441 // Use temporary local to give verifier hint on what type to unify the stack
6443 if (type.IsInterface && true_expr is EmptyCast && false_expr is EmptyCast) {
6444 var temp = ec.GetTemporaryLocal (type);
6445 ec.Emit (OpCodes.Stloc, temp);
6446 ec.Emit (OpCodes.Ldloc, temp);
6447 ec.FreeTemporaryLocal (temp, type);
6450 ec.Emit (OpCodes.Br, end_target);
6451 ec.MarkLabel (false_target);
6452 false_expr.Emit (ec);
6453 ec.MarkLabel (end_target);
6456 public override void FlowAnalysis (FlowAnalysisContext fc)
6458 expr.FlowAnalysisConditional (fc);
6459 var expr_true = fc.DefiniteAssignmentOnTrue;
6460 var expr_false = fc.DefiniteAssignmentOnFalse;
6462 fc.BranchDefiniteAssignment (expr_true);
6463 true_expr.FlowAnalysis (fc);
6464 var true_fc = fc.DefiniteAssignment;
6466 fc.BranchDefiniteAssignment (expr_false);
6467 false_expr.FlowAnalysis (fc);
6469 fc.DefiniteAssignment &= true_fc;
6472 public override void FlowAnalysisConditional (FlowAnalysisContext fc)
6474 expr.FlowAnalysisConditional (fc);
6475 var expr_true = fc.DefiniteAssignmentOnTrue;
6476 var expr_false = fc.DefiniteAssignmentOnFalse;
6478 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_true);
6479 true_expr.FlowAnalysisConditional (fc);
6480 var true_fc = fc.DefiniteAssignment;
6481 var true_da_true = fc.DefiniteAssignmentOnTrue;
6482 var true_da_false = fc.DefiniteAssignmentOnFalse;
6484 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_false);
6485 false_expr.FlowAnalysisConditional (fc);
6487 fc.DefiniteAssignment &= true_fc;
6488 fc.DefiniteAssignmentOnTrue = true_da_true & fc.DefiniteAssignmentOnTrue;
6489 fc.DefiniteAssignmentOnFalse = true_da_false & fc.DefiniteAssignmentOnFalse;
6492 protected override void CloneTo (CloneContext clonectx, Expression t)
6494 Conditional target = (Conditional) t;
6496 target.expr = expr.Clone (clonectx);
6497 target.true_expr = true_expr.Clone (clonectx);
6498 target.false_expr = false_expr.Clone (clonectx);
6502 public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation, IVariableReference
6504 LocalTemporary temp;
6506 #region Abstract
6507 public abstract HoistedVariable GetHoistedVariable (AnonymousExpression ae);
6508 public abstract void SetHasAddressTaken ();
6510 public abstract bool IsLockedByStatement { get; set; }
6512 public abstract bool IsFixed { get; }
6513 public abstract bool IsRef { get; }
6514 public abstract string Name { get; }
6517 // Variable IL data, it has to be protected to encapsulate hoisted variables
6519 protected abstract ILocalVariable Variable { get; }
6522 // Variable flow-analysis data
6524 public abstract VariableInfo VariableInfo { get; }
6525 #endregion
6527 public virtual void AddressOf (EmitContext ec, AddressOp mode)
6529 HoistedVariable hv = GetHoistedVariable (ec);
6530 if (hv != null) {
6531 hv.AddressOf (ec, mode);
6532 return;
6535 Variable.EmitAddressOf (ec);
6538 public override bool ContainsEmitWithAwait ()
6540 return false;
6543 public override Expression CreateExpressionTree (ResolveContext ec)
6545 HoistedVariable hv = GetHoistedVariable (ec);
6546 if (hv != null)
6547 return hv.CreateExpressionTree ();
6549 Arguments arg = new Arguments (1);
6550 arg.Add (new Argument (this));
6551 return CreateExpressionFactoryCall (ec, "Constant", arg);
6554 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
6556 if (IsLockedByStatement) {
6557 rc.Report.Warning (728, 2, loc,
6558 "Possibly incorrect assignment to `{0}' which is the argument to a using or lock statement",
6559 Name);
6562 return this;
6565 public override void Emit (EmitContext ec)
6567 Emit (ec, false);
6570 public override void EmitSideEffect (EmitContext ec)
6572 // do nothing
6576 // This method is used by parameters that are references, that are
6577 // being passed as references: we only want to pass the pointer (that
6578 // is already stored in the parameter, not the address of the pointer,
6579 // and not the value of the variable).
6581 public void EmitLoad (EmitContext ec)
6583 Variable.Emit (ec);
6586 public void Emit (EmitContext ec, bool leave_copy)
6588 HoistedVariable hv = GetHoistedVariable (ec);
6589 if (hv != null) {
6590 hv.Emit (ec, leave_copy);
6591 return;
6594 EmitLoad (ec);
6596 if (IsRef) {
6598 // If we are a reference, we loaded on the stack a pointer
6599 // Now lets load the real value
6601 ec.EmitLoadFromPtr (type);
6604 if (leave_copy) {
6605 ec.Emit (OpCodes.Dup);
6607 if (IsRef) {
6608 temp = new LocalTemporary (Type);
6609 temp.Store (ec);
6614 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
6615 bool prepare_for_load)
6617 HoistedVariable hv = GetHoistedVariable (ec);
6618 if (hv != null) {
6619 hv.EmitAssign (ec, source, leave_copy, prepare_for_load);
6620 return;
6623 bool dereference = IsRef && !(source is ReferenceExpression);
6624 New n_source = source as New;
6625 if (n_source != null && n_source.CanEmitOptimizedLocalTarget (ec)) {
6626 if (!n_source.Emit (ec, this)) {
6627 if (leave_copy) {
6628 EmitLoad (ec);
6629 if (dereference)
6630 ec.EmitLoadFromPtr (type);
6632 return;
6634 } else {
6635 if (dereference)
6636 EmitLoad (ec);
6638 source.Emit (ec);
6641 if (leave_copy) {
6642 ec.Emit (OpCodes.Dup);
6643 if (dereference) {
6644 temp = new LocalTemporary (Type);
6645 temp.Store (ec);
6649 if (dereference)
6650 ec.EmitStoreFromPtr (type);
6651 else
6652 Variable.EmitAssign (ec);
6654 if (temp != null) {
6655 temp.Emit (ec);
6656 temp.Release (ec);
6660 public override Expression EmitToField (EmitContext ec)
6662 HoistedVariable hv = GetHoistedVariable (ec);
6663 if (hv != null) {
6664 return hv.EmitToField (ec);
6667 return base.EmitToField (ec);
6670 public HoistedVariable GetHoistedVariable (ResolveContext rc)
6672 return GetHoistedVariable (rc.CurrentAnonymousMethod);
6675 public HoistedVariable GetHoistedVariable (EmitContext ec)
6677 return GetHoistedVariable (ec.CurrentAnonymousMethod);
6680 public override string GetSignatureForError ()
6682 return Name;
6685 public bool IsHoisted {
6686 get { return GetHoistedVariable ((AnonymousExpression) null) != null; }
6691 // Resolved reference to a local variable
6693 public class LocalVariableReference : VariableReference
6695 public LocalVariable local_info;
6697 public LocalVariableReference (LocalVariable li, Location l)
6699 this.local_info = li;
6700 loc = l;
6703 public override VariableInfo VariableInfo {
6704 get { return local_info.VariableInfo; }
6707 public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
6709 return local_info.HoistedVariant;
6712 #region Properties
6715 // A local variable is always fixed
6717 public override bool IsFixed {
6718 get {
6719 return true;
6723 public override bool IsLockedByStatement {
6724 get {
6725 return local_info.IsLocked;
6727 set {
6728 local_info.IsLocked = value;
6732 public override bool IsRef {
6733 get { return local_info.IsByRef; }
6736 public override string Name {
6737 get { return local_info.Name; }
6740 #endregion
6742 public override void FlowAnalysis (FlowAnalysisContext fc)
6744 VariableInfo variable_info = VariableInfo;
6745 if (variable_info == null)
6746 return;
6748 if (fc.IsDefinitelyAssigned (variable_info))
6749 return;
6751 fc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name);
6752 variable_info.SetAssigned (fc.DefiniteAssignment, true);
6755 public override void SetHasAddressTaken ()
6757 local_info.SetHasAddressTaken ();
6760 void DoResolveBase (ResolveContext ec)
6762 eclass = ExprClass.Variable;
6763 type = local_info.Type;
6766 // If we are referencing a variable from the external block
6767 // flag it for capturing
6769 if (ec.MustCaptureVariable (local_info)) {
6770 if (local_info.AddressTaken) {
6771 AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc);
6772 } else if (local_info.IsFixed) {
6773 ec.Report.Error (1764, loc,
6774 "Cannot use fixed variable `{0}' inside an anonymous method, lambda expression or query expression",
6775 GetSignatureForError ());
6776 } else if (local_info.IsByRef || local_info.Type.IsByRefLike) {
6777 if (ec.CurrentAnonymousMethod is StateMachineInitializer) {
6778 // It's reported later as 4012/4013
6779 } else {
6780 ec.Report.Error (8175, loc,
6781 "Cannot use by-reference variable `{0}' inside an anonymous method, lambda expression, or query expression",
6782 GetSignatureForError ());
6786 if (ec.IsVariableCapturingRequired) {
6787 AnonymousMethodStorey storey = local_info.Block.Explicit.CreateAnonymousMethodStorey (ec);
6788 storey.CaptureLocalVariable (ec, local_info);
6793 protected override Expression DoResolve (ResolveContext ec)
6795 local_info.SetIsUsed ();
6797 DoResolveBase (ec);
6799 if (local_info.Type == InternalType.VarOutType) {
6800 ec.Report.Error (8048, loc, "Cannot use uninitialized variable `{0}'",
6801 GetSignatureForError ());
6803 type = InternalType.ErrorType;
6806 return this;
6809 public override Expression DoResolveLValue (ResolveContext ec, Expression rhs)
6812 // Don't be too pedantic when variable is used as out param or for some broken code
6813 // which uses property/indexer access to run some initialization
6815 if (rhs == EmptyExpression.OutAccess || rhs.eclass == ExprClass.PropertyAccess || rhs.eclass == ExprClass.IndexerAccess)
6816 local_info.SetIsUsed ();
6818 if (local_info.IsReadonly && !ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.UsingInitializerScope)) {
6819 if (local_info.IsByRef) {
6820 // OK because it cannot be reassigned
6821 } else if (rhs == EmptyExpression.LValueMemberAccess) {
6822 // CS1654 already reported
6823 } else {
6824 int code;
6825 string msg;
6826 if (rhs == EmptyExpression.OutAccess) {
6827 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
6828 } else if (rhs == EmptyExpression.LValueMemberOutAccess) {
6829 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
6830 } else if (rhs == EmptyExpression.UnaryAddress) {
6831 code = 459; msg = "Cannot take the address of {1} `{0}'";
6832 } else {
6833 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
6835 ec.Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
6839 if (eclass == ExprClass.Unresolved)
6840 DoResolveBase (ec);
6842 return base.DoResolveLValue (ec, rhs);
6845 public override int GetHashCode ()
6847 return local_info.GetHashCode ();
6850 public override bool Equals (object obj)
6852 LocalVariableReference lvr = obj as LocalVariableReference;
6853 if (lvr == null)
6854 return false;
6856 return local_info == lvr.local_info;
6859 protected override ILocalVariable Variable {
6860 get { return local_info; }
6863 public override string ToString ()
6865 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
6868 protected override void CloneTo (CloneContext clonectx, Expression t)
6870 // Nothing
6874 /// <summary>
6875 /// This represents a reference to a parameter in the intermediate
6876 /// representation.
6877 /// </summary>
6878 public class ParameterReference : VariableReference
6880 protected ParametersBlock.ParameterInfo pi;
6882 public ParameterReference (ParametersBlock.ParameterInfo pi, Location loc)
6884 this.pi = pi;
6885 this.loc = loc;
6888 #region Properties
6890 public override bool IsLockedByStatement {
6891 get {
6892 return pi.IsLocked;
6894 set {
6895 pi.IsLocked = value;
6899 public override bool IsRef {
6900 get { return (pi.Parameter.ModFlags & Parameter.Modifier.RefOutMask) != 0; }
6903 bool HasOutModifier {
6904 get { return (pi.Parameter.ModFlags & Parameter.Modifier.OUT) != 0; }
6907 public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
6909 return pi.Parameter.HoistedVariant;
6913 // A ref or out parameter is classified as a moveable variable, even
6914 // if the argument given for the parameter is a fixed variable
6916 public override bool IsFixed {
6917 get { return !IsRef; }
6920 public override string Name {
6921 get { return Parameter.Name; }
6924 public Parameter Parameter {
6925 get { return pi.Parameter; }
6928 public override VariableInfo VariableInfo {
6929 get { return pi.VariableInfo; }
6932 protected override ILocalVariable Variable {
6933 get { return Parameter; }
6936 #endregion
6938 public override void AddressOf (EmitContext ec, AddressOp mode)
6941 // ParameterReferences might already be a reference
6943 if (IsRef) {
6944 EmitLoad (ec);
6945 return;
6948 base.AddressOf (ec, mode);
6951 public override void SetHasAddressTaken ()
6953 Parameter.HasAddressTaken = true;
6956 bool DoResolveBase (ResolveContext ec)
6958 if (eclass != ExprClass.Unresolved)
6959 return true;
6961 type = pi.ParameterType;
6962 eclass = ExprClass.Variable;
6965 // If we are referencing a parameter from the external block
6966 // flag it for capturing
6968 if (ec.MustCaptureVariable (pi)) {
6969 if (Parameter.HasAddressTaken)
6970 AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc);
6972 if (IsRef) {
6973 ec.Report.Error (1628, loc,
6974 "Parameter `{0}' cannot be used inside `{1}' when using `ref' or `out' modifier",
6975 Name, ec.CurrentAnonymousMethod.ContainerType);
6978 if (ec.IsVariableCapturingRequired && !pi.Block.ParametersBlock.IsExpressionTree) {
6979 AnonymousMethodStorey storey = pi.Block.Explicit.CreateAnonymousMethodStorey (ec);
6980 storey.CaptureParameter (ec, pi, this);
6984 return true;
6987 public override int GetHashCode ()
6989 return Name.GetHashCode ();
6992 public override bool Equals (object obj)
6994 ParameterReference pr = obj as ParameterReference;
6995 if (pr == null)
6996 return false;
6998 return Name == pr.Name;
7001 protected override void CloneTo (CloneContext clonectx, Expression target)
7003 // Nothing to clone
7004 return;
7007 public override Expression CreateExpressionTree (ResolveContext ec)
7009 HoistedVariable hv = GetHoistedVariable (ec);
7010 if (hv != null)
7011 return hv.CreateExpressionTree ();
7013 return Parameter.ExpressionTreeVariableReference ();
7016 protected override Expression DoResolve (ResolveContext ec)
7018 if (!DoResolveBase (ec))
7019 return null;
7021 return this;
7024 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
7026 if (!DoResolveBase (ec))
7027 return null;
7029 if (Parameter.HoistedVariant != null)
7030 Parameter.HoistedVariant.IsAssigned = true;
7032 return base.DoResolveLValue (ec, right_side);
7035 public override void FlowAnalysis (FlowAnalysisContext fc)
7037 VariableInfo variable_info = VariableInfo;
7038 if (variable_info == null)
7039 return;
7041 if (fc.IsDefinitelyAssigned (variable_info))
7042 return;
7044 fc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
7045 fc.SetVariableAssigned (variable_info);
7049 /// <summary>
7050 /// Invocation of methods or delegates.
7051 /// </summary>
7052 public class Invocation : ExpressionStatement
7054 public class Predefined : Invocation
7056 public Predefined (MethodGroupExpr expr, Arguments arguments)
7057 : base (expr, arguments)
7059 this.mg = expr;
7062 protected override MethodGroupExpr DoResolveOverload (ResolveContext rc)
7064 mg.BestCandidate.CheckObsoleteness (rc, loc);
7066 return mg;
7070 protected Arguments arguments;
7071 protected Expression expr;
7072 protected MethodGroupExpr mg;
7073 bool conditional_access_receiver;
7075 public Invocation (Expression expr, Arguments arguments)
7077 this.expr = expr;
7078 this.arguments = arguments;
7079 if (expr != null) {
7080 loc = expr.Location;
7084 #region Properties
7085 public Arguments Arguments {
7086 get {
7087 return arguments;
7091 public Expression Exp {
7092 get {
7093 return expr;
7097 public MethodGroupExpr MethodGroup {
7098 get {
7099 return mg;
7103 public override Location StartLocation {
7104 get {
7105 return expr.StartLocation;
7109 #endregion
7111 public override MethodGroupExpr CanReduceLambda (AnonymousMethodBody body)
7113 if (MethodGroup == null)
7114 return null;
7116 var candidate = MethodGroup.BestCandidate;
7117 if (candidate == null || !(candidate.IsStatic || Exp is This))
7118 return null;
7120 var args_count = arguments == null ? 0 : arguments.Count;
7121 if (args_count != body.Parameters.Count)
7122 return null;
7124 var lambda_parameters = body.Block.Parameters.FixedParameters;
7125 for (int i = 0; i < args_count; ++i) {
7126 var pr = arguments[i].Expr as ParameterReference;
7127 if (pr == null)
7128 return null;
7130 if (lambda_parameters[i] != pr.Parameter)
7131 return null;
7133 if ((lambda_parameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (pr.Parameter.ModFlags & Parameter.Modifier.RefOutMask))
7134 return null;
7137 var emg = MethodGroup as ExtensionMethodGroupExpr;
7138 if (emg != null) {
7139 var mg = MethodGroupExpr.CreatePredefined (candidate, candidate.DeclaringType, MethodGroup.Location);
7140 if (candidate.IsGeneric) {
7141 var targs = new TypeExpression [candidate.Arity];
7142 for (int i = 0; i < targs.Length; ++i) {
7143 targs[i] = new TypeExpression (candidate.TypeArguments[i], MethodGroup.Location);
7146 mg.SetTypeArguments (null, new TypeArguments (targs));
7149 return mg;
7152 return MethodGroup;
7155 protected override void CloneTo (CloneContext clonectx, Expression t)
7157 Invocation target = (Invocation) t;
7159 if (arguments != null)
7160 target.arguments = arguments.Clone (clonectx);
7162 target.expr = expr.Clone (clonectx);
7165 public override bool ContainsEmitWithAwait ()
7167 if (arguments != null && arguments.ContainsEmitWithAwait ())
7168 return true;
7170 return mg.ContainsEmitWithAwait ();
7173 public override Expression CreateExpressionTree (ResolveContext ec)
7175 Expression instance = mg.IsInstance ?
7176 mg.InstanceExpression.CreateExpressionTree (ec) :
7177 new NullLiteral (loc);
7179 var args = Arguments.CreateForExpressionTree (ec, arguments,
7180 instance,
7181 mg.CreateExpressionTree (ec));
7183 return CreateExpressionFactoryCall (ec, "Call", args);
7186 void ResolveConditionalAccessReceiver (ResolveContext rc)
7188 if (!rc.HasSet (ResolveContext.Options.DontSetConditionalAccessReceiver) && expr.HasConditionalAccess ()) {
7189 conditional_access_receiver = true;
7193 bool statement_resolve;
7194 public override ExpressionStatement ResolveStatement (BlockContext bc)
7196 statement_resolve = true;
7197 var es = base.ResolveStatement (bc);
7198 statement_resolve = false;
7200 return es;
7203 protected override Expression DoResolve (ResolveContext rc)
7205 ResolveConditionalAccessReceiver (rc);
7206 return DoResolveInvocation (rc, null);
7209 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
7211 var sn = expr as SimpleName;
7212 if (sn != null && sn.Name == "var" && sn.Arity == 0 && arguments?.Count > 1) {
7213 var variables = new List<BlockVariable> (arguments.Count);
7214 foreach (var arg in arguments) {
7215 var arg_sn = arg.Expr as SimpleName;
7216 if (arg_sn == null || arg_sn.Arity != 0) {
7217 rc.Report.Error (8199, loc, "The syntax `var (...)' as an lvalue is reserved");
7218 return ErrorExpression.Instance;
7221 var lv = new LocalVariable (rc.CurrentBlock, arg_sn.Name, arg.Expr.Location);
7222 rc.CurrentBlock.AddLocalName (lv);
7223 variables.Add (new BlockVariable (new VarExpr (lv.Location), lv));
7226 var res = new TupleDeconstruct (variables, right_side, loc);
7227 return res.Resolve (rc);
7230 if (right_side != null) {
7231 if (eclass != ExprClass.Unresolved)
7232 return this;
7234 var res = DoResolveInvocation (rc, right_side);
7235 if (res == null)
7236 return null;
7238 return res;
7241 return base.DoResolveLValue (rc, right_side);
7244 Expression DoResolveInvocation (ResolveContext ec, Expression rhs)
7246 Expression member_expr;
7247 var atn = expr as ATypeNameExpression;
7249 var flags = default (ResolveContext.FlagsHandle);
7250 if (conditional_access_receiver)
7251 flags = ec.Set (ResolveContext.Options.DontSetConditionalAccessReceiver);
7253 if (atn != null) {
7254 member_expr = atn.LookupNameExpression (ec, MemberLookupRestrictions.InvocableOnly | MemberLookupRestrictions.ReadAccess);
7255 if (member_expr != null) {
7256 var name_of = member_expr as NameOf;
7257 if (name_of != null) {
7258 return name_of.ResolveOverload (ec, arguments);
7261 member_expr = member_expr.Resolve (ec);
7263 } else {
7264 member_expr = expr.Resolve (ec);
7267 if (conditional_access_receiver)
7268 flags.Dispose ();
7270 if (member_expr == null)
7271 return null;
7274 // Next, evaluate all the expressions in the argument list
7276 bool dynamic_arg = false;
7277 if (arguments != null) {
7278 using (ec.With (ResolveContext.Options.DontSetConditionalAccessReceiver, false)) {
7279 arguments.Resolve (ec, out dynamic_arg);
7283 TypeSpec expr_type = member_expr.Type;
7284 if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
7285 return DoResolveDynamic (ec, member_expr);
7287 mg = member_expr as MethodGroupExpr;
7288 Expression invoke = null;
7290 if (mg == null) {
7291 if (expr_type != null && expr_type.IsDelegate) {
7292 invoke = new DelegateInvocation (member_expr, arguments, conditional_access_receiver, loc);
7293 invoke = invoke.Resolve (ec);
7294 if (invoke == null || !dynamic_arg)
7295 return invoke;
7296 } else {
7297 if (member_expr is RuntimeValueExpression) {
7298 ec.Report.Error (Report.RuntimeErrorId, loc, "Cannot invoke a non-delegate type `{0}'",
7299 member_expr.Type.GetSignatureForError ());
7300 return null;
7303 MemberExpr me = member_expr as MemberExpr;
7304 if (me == null) {
7305 member_expr.Error_UnexpectedKind (ec, ResolveFlags.MethodGroup, loc);
7306 return null;
7309 ec.Report.Error (1955, loc, "The member `{0}' cannot be used as method or delegate",
7310 member_expr.GetSignatureForError ());
7311 return null;
7315 if (invoke == null) {
7316 mg = DoResolveOverload (ec);
7317 if (mg == null)
7318 return null;
7321 if (dynamic_arg)
7322 return DoResolveDynamic (ec, member_expr);
7324 var method = mg.BestCandidate;
7325 type = mg.BestCandidateReturnType;
7326 if (conditional_access_receiver && !statement_resolve)
7327 type = LiftMemberType (ec, type);
7329 if (arguments == null && method.DeclaringType.BuiltinType == BuiltinTypeSpec.Type.Object && method.Name == Destructor.MetadataName) {
7330 if (mg.IsBase)
7331 ec.Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
7332 else
7333 ec.Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
7334 return null;
7337 IsSpecialMethodInvocation (ec, method, loc);
7339 eclass = ExprClass.Value;
7341 if (type.Kind == MemberKind.ByRef && rhs != EmptyExpression.OutAccess)
7342 return ByRefDereference.Create (this).Resolve (ec);
7344 return this;
7347 protected virtual Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr)
7349 Arguments args;
7350 DynamicMemberBinder dmb = memberExpr as DynamicMemberBinder;
7351 if (dmb != null) {
7352 args = dmb.Arguments;
7353 if (arguments != null)
7354 args.AddRange (arguments);
7355 } else if (mg == null) {
7356 if (arguments == null)
7357 args = new Arguments (1);
7358 else
7359 args = arguments;
7361 args.Insert (0, new Argument (memberExpr));
7362 this.expr = null;
7363 } else {
7364 if (mg.IsBase) {
7365 ec.Report.Error (1971, loc,
7366 "The base call to method `{0}' cannot be dynamically dispatched. Consider casting the dynamic arguments or eliminating the base access",
7367 mg.Name);
7368 return null;
7371 if (arguments == null)
7372 args = new Arguments (1);
7373 else
7374 args = arguments;
7376 MemberAccess ma = expr as MemberAccess;
7377 if (ma != null) {
7378 var inst = mg.InstanceExpression;
7379 var left_type = inst as TypeExpr;
7380 if (left_type != null) {
7381 args.Insert (0, new Argument (new TypeOf (left_type.Type, loc).Resolve (ec), Argument.AType.DynamicTypeName));
7382 } else if (inst != null) {
7384 // Any value type has to be pass as by-ref to get back the same
7385 // instance on which the member was called
7387 var mod = inst is IMemoryLocation && TypeSpec.IsValueType (inst.Type) ?
7388 Argument.AType.Ref : Argument.AType.None;
7389 args.Insert (0, new Argument (inst.Resolve (ec), mod));
7391 } else { // is SimpleName
7392 if (ec.IsStatic || ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.BaseInitializer)) {
7393 args.Insert (0, new Argument (new TypeOf (ec.CurrentType, loc).Resolve (ec), Argument.AType.DynamicTypeName));
7394 } else {
7395 args.Insert (0, new Argument (new This (loc).Resolve (ec)));
7400 return new DynamicInvocation (expr as ATypeNameExpression, args, conditional_access_receiver, loc).Resolve (ec);
7403 protected virtual MethodGroupExpr DoResolveOverload (ResolveContext ec)
7405 return mg.OverloadResolve (ec, ref arguments, null, OverloadResolver.Restrictions.None);
7408 public override void FlowAnalysis (FlowAnalysisContext fc)
7410 if (mg.IsConditionallyExcluded)
7411 return;
7413 var da = conditional_access_receiver ? fc.BranchDefiniteAssignment () : null;
7415 mg.FlowAnalysis (fc);
7417 if (arguments != null)
7418 arguments.FlowAnalysis (fc);
7420 if (conditional_access_receiver)
7421 fc.DefiniteAssignment = da;
7424 public override string GetSignatureForError ()
7426 return mg.GetSignatureForError ();
7429 public override bool HasConditionalAccess ()
7431 return expr.HasConditionalAccess ();
7435 // If a member is a method or event, or if it is a constant, field or property of either a delegate type
7436 // or the type dynamic, then the member is invocable
7438 public static bool IsMemberInvocable (MemberSpec member)
7440 switch (member.Kind) {
7441 case MemberKind.Event:
7442 return true;
7443 case MemberKind.Field:
7444 case MemberKind.Property:
7445 var m = member as IInterfaceMemberSpec;
7446 return m.MemberType.IsDelegate || m.MemberType.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7447 default:
7448 return false;
7452 public static bool IsSpecialMethodInvocation (ResolveContext ec, MethodSpec method, Location loc)
7454 if (!method.IsReservedMethod)
7455 return false;
7457 if (ec.HasSet (ResolveContext.Options.InvokeSpecialName) || ec.CurrentMemberDefinition.IsCompilerGenerated)
7458 return false;
7460 ec.Report.SymbolRelatedToPreviousError (method);
7461 ec.Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
7462 method.GetSignatureForError ());
7464 return true;
7467 public override void Emit (EmitContext ec)
7469 if (mg.IsConditionallyExcluded)
7470 return;
7472 if (conditional_access_receiver)
7473 mg.EmitCall (ec, arguments, type, false);
7474 else
7475 mg.EmitCall (ec, arguments, false);
7478 public override void EmitPrepare (EmitContext ec)
7480 mg.EmitPrepare (ec);
7482 arguments?.EmitPrepare (ec);
7485 public override void EmitStatement (EmitContext ec)
7487 if (mg.IsConditionallyExcluded)
7488 return;
7490 if (conditional_access_receiver)
7491 mg.EmitCall (ec, arguments, type, true);
7492 else
7493 mg.EmitCall (ec, arguments, true);
7496 public override SLE.Expression MakeExpression (BuilderContext ctx)
7498 return MakeExpression (ctx, mg.InstanceExpression, mg.BestCandidate, arguments);
7501 public static SLE.Expression MakeExpression (BuilderContext ctx, Expression instance, MethodSpec mi, Arguments args)
7503 #if STATIC
7504 throw new NotSupportedException ();
7505 #else
7506 var instance_expr = instance == null ? null : instance.MakeExpression (ctx);
7507 return SLE.Expression.Call (instance_expr, (MethodInfo) mi.GetMetaInfo (), Arguments.MakeExpression (args, ctx));
7508 #endif
7511 public override object Accept (StructuralVisitor visitor)
7513 return visitor.Visit (this);
7518 // Implements simple new expression
7520 public class New : ExpressionStatement, IMemoryLocation
7522 protected Arguments arguments;
7525 // During bootstrap, it contains the RequestedType,
7526 // but if `type' is not null, it *might* contain a NewDelegate
7527 // (because of field multi-initialization)
7529 protected Expression RequestedType;
7531 protected MethodSpec method;
7533 public New (Expression requested_type, Arguments arguments, Location l)
7535 RequestedType = requested_type;
7536 this.arguments = arguments;
7537 loc = l;
7540 #region Properties
7541 public Arguments Arguments {
7542 get {
7543 return arguments;
7548 // Returns true for resolved `new S()' when S does not declare parameterless constructor
7550 public bool IsGeneratedStructConstructor {
7551 get {
7552 return arguments == null && method == null && type.IsStruct && GetType () == typeof (New);
7556 public Expression TypeExpression {
7557 get {
7558 return RequestedType;
7562 #endregion
7564 /// <summary>
7565 /// Converts complex core type syntax like 'new int ()' to simple constant
7566 /// </summary>
7567 public static Constant Constantify (TypeSpec t, Location loc)
7569 switch (t.BuiltinType) {
7570 case BuiltinTypeSpec.Type.Int:
7571 return new IntConstant (t, 0, loc);
7572 case BuiltinTypeSpec.Type.UInt:
7573 return new UIntConstant (t, 0, loc);
7574 case BuiltinTypeSpec.Type.Long:
7575 return new LongConstant (t, 0, loc);
7576 case BuiltinTypeSpec.Type.ULong:
7577 return new ULongConstant (t, 0, loc);
7578 case BuiltinTypeSpec.Type.Float:
7579 return new FloatConstant (t, 0, loc);
7580 case BuiltinTypeSpec.Type.Double:
7581 return new DoubleConstant (t, 0, loc);
7582 case BuiltinTypeSpec.Type.Short:
7583 return new ShortConstant (t, 0, loc);
7584 case BuiltinTypeSpec.Type.UShort:
7585 return new UShortConstant (t, 0, loc);
7586 case BuiltinTypeSpec.Type.SByte:
7587 return new SByteConstant (t, 0, loc);
7588 case BuiltinTypeSpec.Type.Byte:
7589 return new ByteConstant (t, 0, loc);
7590 case BuiltinTypeSpec.Type.Char:
7591 return new CharConstant (t, '\0', loc);
7592 case BuiltinTypeSpec.Type.Bool:
7593 return new BoolConstant (t, false, loc);
7594 case BuiltinTypeSpec.Type.Decimal:
7595 return new DecimalConstant (t, 0, loc);
7598 if (t.IsEnum)
7599 return new EnumConstant (Constantify (EnumSpec.GetUnderlyingType (t), loc), t);
7601 if (t.IsNullableType)
7602 return Nullable.LiftedNull.Create (t, loc);
7604 return null;
7607 public override bool ContainsEmitWithAwait ()
7609 return arguments != null && arguments.ContainsEmitWithAwait ();
7613 // Checks whether the type is an interface that has the
7614 // [ComImport, CoClass] attributes and must be treated
7615 // specially
7617 public Expression CheckComImport (ResolveContext ec)
7619 if (!type.IsInterface)
7620 return null;
7623 // Turn the call into:
7624 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
7626 var real_class = type.MemberDefinition.GetAttributeCoClass ();
7627 if (real_class == null)
7628 return null;
7630 New proxy = new New (new TypeExpression (real_class, loc), arguments, loc);
7631 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
7632 return cast.Resolve (ec);
7635 public override Expression CreateExpressionTree (ResolveContext ec)
7637 Arguments args;
7638 if (method == null) {
7639 args = new Arguments (1);
7640 args.Add (new Argument (new TypeOf (type, loc)));
7641 } else {
7642 args = Arguments.CreateForExpressionTree (ec,
7643 arguments, new TypeOfMethod (method, loc));
7646 return CreateExpressionFactoryCall (ec, "New", args);
7649 protected override Expression DoResolve (ResolveContext ec)
7651 if (RequestedType is TupleTypeExpr) {
7652 ec.Report.Error (8181, loc, "Tuple type cannot be used in an object creation expression. Use a tuple literal expression instead.");
7655 type = RequestedType.ResolveAsType (ec);
7656 if (type == null)
7657 return null;
7659 eclass = ExprClass.Value;
7661 if (type.IsPointer) {
7662 ec.Report.Error (1919, loc, "Unsafe type `{0}' cannot be used in an object creation expression",
7663 type.GetSignatureForError ());
7664 return null;
7667 if (arguments == null) {
7668 Constant c = Constantify (type, RequestedType.Location);
7669 if (c != null)
7670 return ReducedExpression.Create (c, this);
7673 if (type.IsDelegate) {
7674 return (new NewDelegate (type, arguments, loc)).Resolve (ec);
7677 var tparam = type as TypeParameterSpec;
7678 if (tparam != null) {
7680 // Check whether the type of type parameter can be constructed. BaseType can be a struct for method overrides
7681 // where type parameter constraint is inflated to struct
7683 if ((tparam.SpecialConstraint & (SpecialConstraint.Struct | SpecialConstraint.Constructor)) == 0 && !TypeSpec.IsValueType (tparam)) {
7684 ec.Report.Error (304, loc,
7685 "Cannot create an instance of the variable type `{0}' because it does not have the new() constraint",
7686 type.GetSignatureForError ());
7689 if ((arguments != null) && (arguments.Count != 0)) {
7690 ec.Report.Error (417, loc,
7691 "`{0}': cannot provide arguments when creating an instance of a variable type",
7692 type.GetSignatureForError ());
7695 return this;
7698 if (type.IsStatic) {
7699 ec.Report.SymbolRelatedToPreviousError (type);
7700 ec.Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", type.GetSignatureForError ());
7701 return null;
7704 if (type.IsInterface || type.IsAbstract){
7705 if (!TypeManager.IsGenericType (type)) {
7706 RequestedType = CheckComImport (ec);
7707 if (RequestedType != null)
7708 return RequestedType;
7711 ec.Report.SymbolRelatedToPreviousError (type);
7712 ec.Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", type.GetSignatureForError ());
7713 return null;
7716 bool dynamic;
7717 if (arguments != null) {
7718 arguments.Resolve (ec, out dynamic);
7719 } else {
7720 dynamic = false;
7723 method = ConstructorLookup (ec, type, ref arguments, loc);
7725 if (dynamic) {
7726 arguments.Insert (0, new Argument (new TypeOf (type, loc).Resolve (ec), Argument.AType.DynamicTypeName));
7727 return new DynamicConstructorBinder (type, arguments, loc).Resolve (ec);
7730 return this;
7733 void DoEmitTypeParameter (EmitContext ec)
7735 var m = ec.Module.PredefinedMembers.ActivatorCreateInstance.Resolve (loc);
7736 if (m == null)
7737 return;
7739 var ctor_factory = m.MakeGenericMethod (ec.MemberContext, type);
7740 ec.Emit (OpCodes.Call, ctor_factory);
7744 // This Emit can be invoked in two contexts:
7745 // * As a mechanism that will leave a value on the stack (new object)
7746 // * As one that wont (init struct)
7748 // If we are dealing with a ValueType, we have a few
7749 // situations to deal with:
7751 // * The target is a ValueType, and we have been provided
7752 // the instance (this is easy, we are being assigned).
7754 // * The target of New is being passed as an argument,
7755 // to a boxing operation or a function that takes a
7756 // ValueType.
7758 // In this case, we need to create a temporary variable
7759 // that is the argument of New.
7761 // Returns whether a value is left on the stack
7763 // *** Implementation note ***
7765 // To benefit from this optimization, each assignable expression
7766 // has to manually cast to New and call this Emit.
7768 // TODO: It's worth to implement it for arrays and fields
7770 public virtual bool Emit (EmitContext ec, IMemoryLocation target)
7772 bool is_value_type = type.IsStructOrEnum;
7773 VariableReference vr = target as VariableReference;
7775 bool prepare_await = ec.HasSet (BuilderContext.Options.AsyncBody) && arguments?.ContainsEmitWithAwait () == true;
7777 if (target != null && is_value_type && (vr != null || method == null)) {
7778 if (prepare_await) {
7779 arguments = arguments.Emit (ec, false, true);
7780 prepare_await = false;
7783 target.AddressOf (ec, AddressOp.Store);
7784 } else if (vr != null && vr.IsRef) {
7785 vr.EmitLoad (ec);
7788 if (arguments != null) {
7789 if (prepare_await)
7790 arguments = arguments.Emit (ec, false, true);
7792 arguments.Emit (ec);
7795 if (is_value_type) {
7796 if (method == null) {
7797 ec.Emit (OpCodes.Initobj, type);
7798 return false;
7801 if (vr != null) {
7802 ec.MarkCallEntry (loc);
7803 ec.Emit (OpCodes.Call, method);
7804 return false;
7808 if (type is TypeParameterSpec) {
7809 DoEmitTypeParameter (ec);
7810 return true;
7813 ec.MarkCallEntry (loc);
7814 ec.Emit (OpCodes.Newobj, method);
7815 return true;
7818 public override void Emit (EmitContext ec)
7820 LocalTemporary v = null;
7821 if (method == null && type.IsStructOrEnum) {
7822 // TODO: Use temporary variable from pool
7823 v = new LocalTemporary (type);
7826 if (!Emit (ec, v))
7827 v.Emit (ec);
7830 public override void EmitStatement (EmitContext ec)
7832 LocalTemporary v = null;
7833 if (method == null && TypeSpec.IsValueType (type)) {
7834 // TODO: Use temporary variable from pool
7835 v = new LocalTemporary (type);
7838 if (Emit (ec, v))
7839 ec.Emit (OpCodes.Pop);
7842 public virtual bool CanEmitOptimizedLocalTarget (EmitContext ec)
7844 return true;
7847 public override void FlowAnalysis (FlowAnalysisContext fc)
7849 if (arguments != null)
7850 arguments.FlowAnalysis (fc);
7853 public void AddressOf (EmitContext ec, AddressOp mode)
7855 EmitAddressOf (ec, mode);
7858 protected virtual IMemoryLocation EmitAddressOf (EmitContext ec, AddressOp mode)
7860 LocalTemporary value_target = new LocalTemporary (type);
7862 if (type is TypeParameterSpec) {
7863 DoEmitTypeParameter (ec);
7864 value_target.Store (ec);
7865 value_target.AddressOf (ec, mode);
7866 return value_target;
7869 value_target.AddressOf (ec, AddressOp.Store);
7871 if (method == null) {
7872 ec.Emit (OpCodes.Initobj, type);
7873 } else {
7874 if (arguments != null)
7875 arguments.Emit (ec);
7877 ec.Emit (OpCodes.Call, method);
7880 value_target.AddressOf (ec, mode);
7881 return value_target;
7884 protected override void CloneTo (CloneContext clonectx, Expression t)
7886 New target = (New) t;
7888 target.RequestedType = RequestedType.Clone (clonectx);
7889 if (arguments != null){
7890 target.arguments = arguments.Clone (clonectx);
7894 public override SLE.Expression MakeExpression (BuilderContext ctx)
7896 #if STATIC
7897 return base.MakeExpression (ctx);
7898 #else
7899 return SLE.Expression.New ((ConstructorInfo) method.GetMetaInfo (), Arguments.MakeExpression (arguments, ctx));
7900 #endif
7903 public override object Accept (StructuralVisitor visitor)
7905 return visitor.Visit (this);
7910 // Array initializer expression, the expression is allowed in
7911 // variable or field initialization only which makes it tricky as
7912 // the type has to be infered based on the context either from field
7913 // type or variable type (think of multiple declarators)
7915 public class ArrayInitializer : Expression
7917 List<Expression> elements;
7918 BlockVariable variable;
7920 public ArrayInitializer (List<Expression> init, Location loc)
7922 elements = init;
7923 this.loc = loc;
7926 public ArrayInitializer (int count, Location loc)
7927 : this (new List<Expression> (count), loc)
7931 public ArrayInitializer (Location loc)
7932 : this (4, loc)
7936 #region Properties
7938 public int Count {
7939 get { return elements.Count; }
7942 public List<Expression> Elements {
7943 get {
7944 return elements;
7948 public Expression this [int index] {
7949 get {
7950 return elements [index];
7954 public BlockVariable VariableDeclaration {
7955 get {
7956 return variable;
7958 set {
7959 variable = value;
7963 #endregion
7965 public void Add (Expression expr)
7967 elements.Add (expr);
7970 public override bool ContainsEmitWithAwait ()
7972 throw new NotSupportedException ();
7975 public override Expression CreateExpressionTree (ResolveContext ec)
7977 throw new NotSupportedException ("ET");
7980 protected override void CloneTo (CloneContext clonectx, Expression t)
7982 var target = (ArrayInitializer) t;
7984 target.elements = new List<Expression> (elements.Count);
7985 foreach (var element in elements)
7986 target.elements.Add (element.Clone (clonectx));
7989 protected override Expression DoResolve (ResolveContext rc)
7991 var current_field = rc.CurrentMemberDefinition as FieldBase;
7992 TypeExpression type;
7993 if (current_field != null && rc.CurrentAnonymousMethod == null) {
7994 type = new TypeExpression (current_field.MemberType, current_field.Location);
7995 } else if (variable != null) {
7996 if (variable.TypeExpression is VarExpr) {
7997 rc.Report.Error (820, loc, "An implicitly typed local variable declarator cannot use an array initializer");
7998 return EmptyExpression.Null;
8001 type = new TypeExpression (variable.Variable.Type, variable.Variable.Location);
8002 } else {
8003 throw new NotImplementedException ("Unexpected array initializer context");
8006 return new ArrayCreation (type, this).Resolve (rc);
8009 public override void Emit (EmitContext ec)
8011 throw new InternalErrorException ("Missing Resolve call");
8014 public override void FlowAnalysis (FlowAnalysisContext fc)
8016 throw new InternalErrorException ("Missing Resolve call");
8019 public override object Accept (StructuralVisitor visitor)
8021 return visitor.Visit (this);
8025 /// <summary>
8026 /// 14.5.10.2: Represents an array creation expression.
8027 /// </summary>
8029 /// <remarks>
8030 /// There are two possible scenarios here: one is an array creation
8031 /// expression that specifies the dimensions and optionally the
8032 /// initialization data and the other which does not need dimensions
8033 /// specified but where initialization data is mandatory.
8034 /// </remarks>
8035 public class ArrayCreation : Expression
8037 FullNamedExpression requested_base_type;
8038 ArrayInitializer initializers;
8041 // The list of Argument types.
8042 // This is used to construct the `newarray' or constructor signature
8044 protected List<Expression> arguments;
8046 protected TypeSpec array_element_type;
8047 int num_arguments;
8048 protected int dimensions;
8049 protected readonly ComposedTypeSpecifier rank;
8050 Expression first_emit;
8051 LocalTemporary first_emit_temp;
8053 protected List<Expression> array_data;
8055 Dictionary<int, int> bounds;
8057 #if STATIC
8058 // The number of constants in array initializers
8059 int const_initializers_count;
8060 bool only_constant_initializers;
8061 #endif
8062 public ArrayCreation (FullNamedExpression requested_base_type, List<Expression> exprs, ComposedTypeSpecifier rank, ArrayInitializer initializers, Location l)
8063 : this (requested_base_type, rank, initializers, l)
8065 arguments = new List<Expression> (exprs);
8066 num_arguments = arguments.Count;
8070 // For expressions like int[] foo = new int[] { 1, 2, 3 };
8072 public ArrayCreation (FullNamedExpression requested_base_type, ComposedTypeSpecifier rank, ArrayInitializer initializers, Location loc)
8074 this.requested_base_type = requested_base_type;
8075 this.rank = rank;
8076 this.initializers = initializers;
8077 this.loc = loc;
8079 if (rank != null)
8080 num_arguments = rank.Dimension;
8084 // For compiler generated single dimensional arrays only
8086 public ArrayCreation (FullNamedExpression requested_base_type, ArrayInitializer initializers, Location loc)
8087 : this (requested_base_type, ComposedTypeSpecifier.SingleDimension, initializers, loc)
8092 // For expressions like int[] foo = { 1, 2, 3 };
8094 public ArrayCreation (FullNamedExpression requested_base_type, ArrayInitializer initializers)
8095 : this (requested_base_type, null, initializers, initializers.Location)
8099 public bool NoEmptyInterpolation { get; set; }
8101 public ComposedTypeSpecifier Rank {
8102 get {
8103 return this.rank;
8107 public FullNamedExpression TypeExpression {
8108 get {
8109 return this.requested_base_type;
8113 public ArrayInitializer Initializers {
8114 get {
8115 return this.initializers;
8119 bool CheckIndices (ResolveContext ec, ArrayInitializer probe, int idx, bool specified_dims, int child_bounds)
8121 if (initializers != null && bounds == null) {
8123 // We use this to store all the data values in the order in which we
8124 // will need to store them in the byte blob later
8126 array_data = new List<Expression> (probe.Count);
8127 bounds = new Dictionary<int, int> ();
8130 if (specified_dims) {
8131 Expression a = arguments [idx];
8132 a = a.Resolve (ec);
8133 if (a == null)
8134 return false;
8136 a = ConvertExpressionToArrayIndex (ec, a);
8137 if (a == null)
8138 return false;
8140 arguments[idx] = a;
8142 if (initializers != null) {
8143 Constant c = a as Constant;
8144 if (c == null && a is ArrayIndexCast)
8145 c = ((ArrayIndexCast) a).Child as Constant;
8147 if (c == null) {
8148 ec.Report.Error (150, a.Location, "A constant value is expected");
8149 return false;
8152 int value;
8153 try {
8154 value = System.Convert.ToInt32 (c.GetValue ());
8155 } catch {
8156 ec.Report.Error (150, a.Location, "A constant value is expected");
8157 return false;
8160 // TODO: probe.Count does not fit ulong in
8161 if (value != probe.Count) {
8162 ec.Report.Error (847, loc, "An array initializer of length `{0}' was expected", value.ToString ());
8163 return false;
8166 bounds[idx] = value;
8170 if (initializers == null)
8171 return true;
8173 for (int i = 0; i < probe.Count; ++i) {
8174 var o = probe [i];
8175 if (o is ArrayInitializer) {
8176 var sub_probe = o as ArrayInitializer;
8177 if (idx + 1 >= dimensions){
8178 ec.Report.Error (623, loc, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
8179 return false;
8182 // When we don't have explicitly specified dimensions, record whatever dimension we first encounter at each level
8183 if (!bounds.ContainsKey(idx + 1))
8184 bounds[idx + 1] = sub_probe.Count;
8186 if (bounds[idx + 1] != sub_probe.Count) {
8187 ec.Report.Error(847, sub_probe.Location, "An array initializer of length `{0}' was expected", bounds[idx + 1].ToString());
8188 return false;
8191 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims, child_bounds - 1);
8192 if (!ret)
8193 return false;
8194 } else if (child_bounds > 1) {
8195 ec.Report.Error (846, o.Location, "A nested array initializer was expected");
8196 } else {
8197 Expression element = ResolveArrayElement (ec, o);
8198 if (element == null)
8199 continue;
8200 #if STATIC
8201 // Initializers with the default values can be ignored
8202 Constant c = element as Constant;
8203 if (c != null) {
8204 if (!c.IsDefaultInitializer (array_element_type)) {
8205 ++const_initializers_count;
8207 } else {
8208 only_constant_initializers = false;
8210 #endif
8211 array_data.Add (element);
8215 return true;
8218 public override bool ContainsEmitWithAwait ()
8220 foreach (var arg in arguments) {
8221 if (arg.ContainsEmitWithAwait ())
8222 return true;
8225 return InitializersContainAwait ();
8228 public override Expression CreateExpressionTree (ResolveContext ec)
8230 Arguments args;
8232 if (array_data == null) {
8233 args = new Arguments (arguments.Count + 1);
8234 args.Add (new Argument (new TypeOf (array_element_type, loc)));
8235 foreach (Expression a in arguments)
8236 args.Add (new Argument (a.CreateExpressionTree (ec)));
8238 return CreateExpressionFactoryCall (ec, "NewArrayBounds", args);
8241 if (dimensions > 1) {
8242 ec.Report.Error (838, loc, "An expression tree cannot contain a multidimensional array initializer");
8243 return null;
8246 args = new Arguments (array_data == null ? 1 : array_data.Count + 1);
8247 args.Add (new Argument (new TypeOf (array_element_type, loc)));
8248 if (array_data != null) {
8249 for (int i = 0; i < array_data.Count; ++i) {
8250 Expression e = array_data [i];
8251 args.Add (new Argument (e.CreateExpressionTree (ec)));
8255 return CreateExpressionFactoryCall (ec, "NewArrayInit", args);
8258 void UpdateIndices (ResolveContext rc)
8260 int i = 0;
8261 for (var probe = initializers; probe != null;) {
8262 Expression e = new IntConstant (rc.BuiltinTypes, probe.Count, Location.Null);
8263 arguments.Add (e);
8264 bounds[i++] = probe.Count;
8266 if (probe.Count > 0 && probe [0] is ArrayInitializer) {
8267 probe = (ArrayInitializer) probe[0];
8268 } else if (dimensions > i) {
8269 continue;
8270 } else {
8271 return;
8276 protected override void Error_NegativeArrayIndex (ResolveContext ec, Location loc)
8278 ec.Report.Error (248, loc, "Cannot create an array with a negative size");
8281 public override void FlowAnalysis (FlowAnalysisContext fc)
8283 foreach (var arg in arguments)
8284 arg.FlowAnalysis (fc);
8286 if (array_data != null) {
8287 foreach (var ad in array_data)
8288 ad.FlowAnalysis (fc);
8292 bool InitializersContainAwait ()
8294 if (array_data == null)
8295 return false;
8297 foreach (var expr in array_data) {
8298 if (expr.ContainsEmitWithAwait ())
8299 return true;
8302 return false;
8305 protected virtual Expression ResolveArrayElement (ResolveContext ec, Expression element)
8307 element = element.Resolve (ec);
8308 if (element == null)
8309 return null;
8311 var te = element as CompoundAssign.TargetExpression;
8312 if (te != null) {
8313 for (int i = 1; i < initializers.Count; ++i) {
8314 if (initializers [i].ContainsEmitWithAwait ()) {
8315 te.RequiresEmitWithAwait = true;
8316 break;
8320 if (!te.RequiresEmitWithAwait) {
8321 if (first_emit != null)
8322 throw new InternalErrorException ("Can only handle one mutator at a time");
8323 first_emit = element;
8324 element = first_emit_temp = new LocalTemporary (element.Type);
8328 return Convert.ImplicitConversionRequired (
8329 ec, element, array_element_type, loc);
8332 protected bool ResolveInitializers (ResolveContext ec)
8334 #if STATIC
8335 only_constant_initializers = true;
8336 #endif
8338 if (arguments != null) {
8339 bool res = true;
8340 for (int i = 0; i < arguments.Count; ++i) {
8341 res &= CheckIndices (ec, initializers, i, true, dimensions);
8342 if (initializers != null)
8343 break;
8346 return res;
8349 arguments = new List<Expression> ();
8351 if (!CheckIndices (ec, initializers, 0, false, dimensions))
8352 return false;
8354 UpdateIndices (ec);
8356 return true;
8360 // Resolved the type of the array
8362 bool ResolveArrayType (ResolveContext ec)
8365 // Lookup the type
8367 FullNamedExpression array_type_expr;
8368 if (num_arguments > 0) {
8369 array_type_expr = new ComposedCast (requested_base_type, rank);
8370 } else {
8371 array_type_expr = requested_base_type;
8374 type = array_type_expr.ResolveAsType (ec);
8375 if (array_type_expr == null)
8376 return false;
8378 var ac = type as ArrayContainer;
8379 if (ac == null) {
8380 ec.Report.Error (622, loc, "Can only use array initializer expressions to assign to array types. Try using a new expression instead");
8381 return false;
8384 array_element_type = ac.Element;
8385 dimensions = ac.Rank;
8387 return true;
8390 protected override Expression DoResolve (ResolveContext ec)
8392 if (type != null)
8393 return this;
8395 if (!ResolveArrayType (ec))
8396 return null;
8399 // validate the initializers and fill in any missing bits
8401 if (!ResolveInitializers (ec))
8402 return null;
8404 eclass = ExprClass.Value;
8405 return this;
8408 byte [] MakeByteBlob ()
8410 int factor;
8411 byte [] data;
8412 byte [] element;
8413 int count = array_data.Count;
8415 TypeSpec element_type = array_element_type;
8416 if (element_type.IsEnum)
8417 element_type = EnumSpec.GetUnderlyingType (element_type);
8419 factor = BuiltinTypeSpec.GetSize (element_type);
8420 if (factor == 0)
8421 throw new Exception ("unrecognized type in MakeByteBlob: " + element_type);
8423 data = new byte [(count * factor + 3) & ~3];
8424 int idx = 0;
8426 for (int i = 0; i < count; ++i) {
8427 var c = array_data[i] as Constant;
8428 if (c == null) {
8429 idx += factor;
8430 continue;
8433 object v = c.GetValue ();
8435 switch (element_type.BuiltinType) {
8436 case BuiltinTypeSpec.Type.Long:
8437 long lval = (long) v;
8439 for (int j = 0; j < factor; ++j) {
8440 data[idx + j] = (byte) (lval & 0xFF);
8441 lval = (lval >> 8);
8443 break;
8444 case BuiltinTypeSpec.Type.ULong:
8445 ulong ulval = (ulong) v;
8447 for (int j = 0; j < factor; ++j) {
8448 data[idx + j] = (byte) (ulval & 0xFF);
8449 ulval = (ulval >> 8);
8451 break;
8452 case BuiltinTypeSpec.Type.Float:
8453 var fval = SingleConverter.SingleToInt32Bits((float) v);
8455 data[idx] = (byte) (fval & 0xff);
8456 data[idx + 1] = (byte) ((fval >> 8) & 0xff);
8457 data[idx + 2] = (byte) ((fval >> 16) & 0xff);
8458 data[idx + 3] = (byte) (fval >> 24);
8459 break;
8460 case BuiltinTypeSpec.Type.Double:
8461 element = BitConverter.GetBytes ((double) v);
8463 for (int j = 0; j < factor; ++j)
8464 data[idx + j] = element[j];
8466 // FIXME: Handle the ARM float format.
8467 if (!BitConverter.IsLittleEndian)
8468 System.Array.Reverse (data, idx, 8);
8469 break;
8470 case BuiltinTypeSpec.Type.Char:
8471 int chval = (int) ((char) v);
8473 data[idx] = (byte) (chval & 0xff);
8474 data[idx + 1] = (byte) (chval >> 8);
8475 break;
8476 case BuiltinTypeSpec.Type.Short:
8477 int sval = (int) ((short) v);
8479 data[idx] = (byte) (sval & 0xff);
8480 data[idx + 1] = (byte) (sval >> 8);
8481 break;
8482 case BuiltinTypeSpec.Type.UShort:
8483 int usval = (int) ((ushort) v);
8485 data[idx] = (byte) (usval & 0xff);
8486 data[idx + 1] = (byte) (usval >> 8);
8487 break;
8488 case BuiltinTypeSpec.Type.Int:
8489 int val = (int) v;
8491 data[idx] = (byte) (val & 0xff);
8492 data[idx + 1] = (byte) ((val >> 8) & 0xff);
8493 data[idx + 2] = (byte) ((val >> 16) & 0xff);
8494 data[idx + 3] = (byte) (val >> 24);
8495 break;
8496 case BuiltinTypeSpec.Type.UInt:
8497 uint uval = (uint) v;
8499 data[idx] = (byte) (uval & 0xff);
8500 data[idx + 1] = (byte) ((uval >> 8) & 0xff);
8501 data[idx + 2] = (byte) ((uval >> 16) & 0xff);
8502 data[idx + 3] = (byte) (uval >> 24);
8503 break;
8504 case BuiltinTypeSpec.Type.SByte:
8505 data[idx] = (byte) (sbyte) v;
8506 break;
8507 case BuiltinTypeSpec.Type.Byte:
8508 data[idx] = (byte) v;
8509 break;
8510 case BuiltinTypeSpec.Type.Bool:
8511 data[idx] = (byte) ((bool) v ? 1 : 0);
8512 break;
8513 case BuiltinTypeSpec.Type.Decimal:
8514 int[] bits = Decimal.GetBits ((decimal) v);
8515 int p = idx;
8517 // FIXME: For some reason, this doesn't work on the MS runtime.
8518 int[] nbits = new int[4];
8519 nbits[0] = bits[3];
8520 nbits[1] = bits[2];
8521 nbits[2] = bits[0];
8522 nbits[3] = bits[1];
8524 for (int j = 0; j < 4; j++) {
8525 data[p++] = (byte) (nbits[j] & 0xff);
8526 data[p++] = (byte) ((nbits[j] >> 8) & 0xff);
8527 data[p++] = (byte) ((nbits[j] >> 16) & 0xff);
8528 data[p++] = (byte) (nbits[j] >> 24);
8530 break;
8531 default:
8532 throw new Exception ("Unrecognized type in MakeByteBlob: " + element_type);
8535 idx += factor;
8538 return data;
8541 public override SLE.Expression MakeExpression (BuilderContext ctx)
8543 #if STATIC
8544 return base.MakeExpression (ctx);
8545 #else
8546 var initializers = new SLE.Expression [array_data.Count];
8547 for (var i = 0; i < initializers.Length; i++) {
8548 if (array_data [i] == null)
8549 initializers [i] = SLE.Expression.Default (array_element_type.GetMetaInfo ());
8550 else
8551 initializers [i] = array_data [i].MakeExpression (ctx);
8554 return SLE.Expression.NewArrayInit (array_element_type.GetMetaInfo (), initializers);
8555 #endif
8557 #if STATIC
8559 // Emits the initializers for the array
8561 void EmitStaticInitializers (EmitContext ec, FieldExpr stackArray)
8563 var m = ec.Module.PredefinedMembers.RuntimeHelpersInitializeArray.Resolve (loc);
8564 if (m == null)
8565 return;
8568 // First, the static data
8570 byte [] data = MakeByteBlob ();
8571 var fb = ec.CurrentTypeDefinition.Module.MakeStaticData (data, loc);
8573 if (stackArray == null) {
8574 ec.Emit (OpCodes.Dup);
8575 } else {
8576 stackArray.Emit (ec);
8579 ec.Emit (OpCodes.Ldtoken, fb);
8580 ec.Emit (OpCodes.Call, m);
8582 #endif
8585 // Emits pieces of the array that can not be computed at compile
8586 // time (variables and string locations).
8588 // This always expect the top value on the stack to be the array
8590 void EmitDynamicInitializers (EmitContext ec, bool emitConstants, StackFieldExpr stackArray)
8592 int dims = bounds.Count;
8593 var current_pos = new int [dims];
8595 for (int i = 0; i < array_data.Count; i++){
8597 Expression e = array_data [i];
8598 var c = e as Constant;
8600 // Constant can be initialized via StaticInitializer
8601 if (c == null || (c != null && emitConstants && !c.IsDefaultInitializer (array_element_type))) {
8603 var etype = e.Type;
8605 if (stackArray != null) {
8606 if (e.ContainsEmitWithAwait ()) {
8607 e = e.EmitToField (ec);
8610 stackArray.EmitLoad (ec);
8611 } else {
8612 ec.Emit (OpCodes.Dup);
8615 for (int idx = 0; idx < dims; idx++)
8616 ec.EmitInt (current_pos [idx]);
8619 // If we are dealing with a struct, get the
8620 // address of it, so we can store it.
8622 if (dims == 1 && etype.IsStruct && !BuiltinTypeSpec.IsPrimitiveType (etype))
8623 ec.Emit (OpCodes.Ldelema, etype);
8625 e.Emit (ec);
8627 ec.EmitArrayStore ((ArrayContainer) type);
8631 // Advance counter
8633 for (int j = dims - 1; j >= 0; j--){
8634 current_pos [j]++;
8635 if (current_pos [j] < bounds [j])
8636 break;
8637 current_pos [j] = 0;
8641 if (stackArray != null)
8642 stackArray.PrepareCleanup (ec);
8645 public override void Emit (EmitContext ec)
8647 if (!NoEmptyInterpolation && EmitOptimizedEmpty (ec))
8648 return;
8650 var await_field = EmitToFieldSource (ec);
8651 if (await_field != null)
8652 await_field.Emit (ec);
8655 bool EmitOptimizedEmpty (EmitContext ec)
8657 if (arguments.Count != 1 || dimensions != 1)
8658 return false;
8660 var c = arguments [0] as Constant;
8661 if (c == null || !c.IsZeroInteger)
8662 return false;
8664 var m = ec.Module.PredefinedMembers.ArrayEmpty.Get ();
8665 if (m == null || ec.CurrentType.MemberDefinition.DeclaringAssembly == m.DeclaringType.MemberDefinition.DeclaringAssembly)
8666 return false;
8668 m = m.MakeGenericMethod (ec.MemberContext, array_element_type);
8669 ec.Emit (OpCodes.Call, m);
8670 return true;
8673 protected sealed override FieldExpr EmitToFieldSource (EmitContext ec)
8675 if (first_emit != null) {
8676 first_emit.Emit (ec);
8677 first_emit_temp.Store (ec);
8680 StackFieldExpr await_stack_field;
8681 if (ec.HasSet (BuilderContext.Options.AsyncBody) && InitializersContainAwait ()) {
8682 await_stack_field = ec.GetTemporaryField (type);
8683 ec.EmitThis ();
8684 } else {
8685 await_stack_field = null;
8688 EmitExpressionsList (ec, arguments);
8690 ec.EmitArrayNew ((ArrayContainer) type);
8692 if (initializers == null)
8693 return await_stack_field;
8695 if (await_stack_field != null)
8696 await_stack_field.EmitAssignFromStack (ec);
8698 #if STATIC
8700 // Emit static initializer for arrays which contain more than 2 items and
8701 // the static initializer will initialize at least 25% of array values or there
8702 // is more than 10 items to be initialized
8704 // NOTE: const_initializers_count does not contain default constant values.
8706 if (const_initializers_count > 2 && (array_data.Count > 10 || const_initializers_count * 4 > (array_data.Count)) &&
8707 (BuiltinTypeSpec.IsPrimitiveType (array_element_type) || array_element_type.IsEnum)) {
8708 EmitStaticInitializers (ec, await_stack_field);
8710 if (!only_constant_initializers)
8711 EmitDynamicInitializers (ec, false, await_stack_field);
8712 } else
8713 #endif
8715 EmitDynamicInitializers (ec, true, await_stack_field);
8718 if (first_emit_temp != null)
8719 first_emit_temp.Release (ec);
8721 return await_stack_field;
8724 public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType)
8726 // no multi dimensional or jagged arrays
8727 if (arguments.Count != 1 || array_element_type.IsArray) {
8728 base.EncodeAttributeValue (rc, enc, targetType, parameterType);
8729 return;
8732 // No array covariance, except for array -> object
8733 if (type != targetType) {
8734 if (targetType.BuiltinType != BuiltinTypeSpec.Type.Object) {
8735 base.EncodeAttributeValue (rc, enc, targetType, parameterType);
8736 return;
8739 if (enc.Encode (type) == AttributeEncoder.EncodedTypeProperties.DynamicType) {
8740 Attribute.Error_AttributeArgumentIsDynamic (rc, loc);
8741 return;
8745 // Single dimensional array of 0 size
8746 if (array_data == null) {
8747 IntConstant ic = arguments[0] as IntConstant;
8748 if (ic == null || !ic.IsDefaultValue) {
8749 base.EncodeAttributeValue (rc, enc, targetType, parameterType);
8750 } else {
8751 enc.Encode (0);
8754 return;
8757 enc.Encode (array_data.Count);
8758 foreach (var element in array_data) {
8759 element.EncodeAttributeValue (rc, enc, array_element_type, parameterType);
8763 protected override void CloneTo (CloneContext clonectx, Expression t)
8765 ArrayCreation target = (ArrayCreation) t;
8767 if (requested_base_type != null)
8768 target.requested_base_type = (FullNamedExpression)requested_base_type.Clone (clonectx);
8770 if (arguments != null){
8771 target.arguments = new List<Expression> (arguments.Count);
8772 foreach (Expression e in arguments)
8773 target.arguments.Add (e.Clone (clonectx));
8776 if (initializers != null)
8777 target.initializers = (ArrayInitializer) initializers.Clone (clonectx);
8780 public override object Accept (StructuralVisitor visitor)
8782 return visitor.Visit (this);
8787 // Represents an implicitly typed array epxression
8789 class ImplicitlyTypedArrayCreation : ArrayCreation
8791 TypeInferenceContext best_type_inference;
8793 public ImplicitlyTypedArrayCreation (ComposedTypeSpecifier rank, ArrayInitializer initializers, Location loc)
8794 : base (null, rank, initializers, loc)
8798 public ImplicitlyTypedArrayCreation (ArrayInitializer initializers, Location loc)
8799 : base (null, initializers, loc)
8803 protected override Expression DoResolve (ResolveContext ec)
8805 if (type != null)
8806 return this;
8808 dimensions = rank.Dimension;
8810 best_type_inference = new TypeInferenceContext ();
8812 if (!ResolveInitializers (ec))
8813 return null;
8815 best_type_inference.FixAllTypes (ec);
8816 array_element_type = best_type_inference.InferredTypeArguments[0];
8817 best_type_inference = null;
8819 if (array_element_type == null || InternalType.HasNoType (array_element_type) || arguments.Count != rank.Dimension) {
8820 ec.Report.Error (826, loc,
8821 "The type of an implicitly typed array cannot be inferred from the initializer. Try specifying array type explicitly");
8822 return null;
8826 // At this point we found common base type for all initializer elements
8827 // but we have to be sure that all static initializer elements are of
8828 // same type
8830 UnifyInitializerElement (ec);
8832 type = ArrayContainer.MakeType (ec.Module, array_element_type, dimensions);
8833 eclass = ExprClass.Value;
8834 return this;
8838 // Converts static initializer only
8840 void UnifyInitializerElement (ResolveContext ec)
8842 for (int i = 0; i < array_data.Count; ++i) {
8843 Expression e = array_data[i];
8844 if (e != null)
8845 array_data [i] = Convert.ImplicitConversion (ec, e, array_element_type, Location.Null);
8849 protected override Expression ResolveArrayElement (ResolveContext ec, Expression element)
8851 element = element.Resolve (ec);
8852 if (element != null)
8853 best_type_inference.AddCommonTypeBound (element.Type);
8855 return element;
8859 sealed class CompilerGeneratedThis : This
8861 public CompilerGeneratedThis (TypeSpec type, Location loc)
8862 : base (loc)
8864 this.type = type;
8867 protected override Expression DoResolve (ResolveContext rc)
8869 eclass = ExprClass.Variable;
8871 var block = rc.CurrentBlock;
8872 if (block != null) {
8873 var top = block.ParametersBlock.TopBlock;
8874 if (top.ThisVariable != null)
8875 variable_info = top.ThisVariable.VariableInfo;
8879 return this;
8882 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
8884 return DoResolve (rc);
8887 public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
8889 return null;
8893 /// <summary>
8894 /// Represents the `this' construct
8895 /// </summary>
8897 public class This : VariableReference
8899 sealed class ThisVariable : ILocalVariable
8901 public static readonly ILocalVariable Instance = new ThisVariable ();
8903 public void Emit (EmitContext ec)
8905 ec.EmitThis ();
8908 public void EmitAssign (EmitContext ec)
8910 throw new InvalidOperationException ();
8913 public void EmitAddressOf (EmitContext ec)
8915 ec.EmitThis ();
8919 protected VariableInfo variable_info;
8921 public This (Location loc)
8923 this.loc = loc;
8926 #region Properties
8928 public override string Name {
8929 get { return "this"; }
8932 public override bool IsLockedByStatement {
8933 get {
8934 return false;
8936 set {
8940 public override bool IsRef {
8941 get { return type.IsStruct; }
8944 public override bool IsSideEffectFree {
8945 get {
8946 return true;
8950 protected override ILocalVariable Variable {
8951 get { return ThisVariable.Instance; }
8954 public override VariableInfo VariableInfo {
8955 get { return variable_info; }
8958 public override bool IsFixed {
8959 get { return false; }
8962 #endregion
8964 void CheckStructThisDefiniteAssignment (FlowAnalysisContext fc)
8967 // It's null for all cases when we don't need to check `this'
8968 // definitive assignment
8970 if (variable_info == null)
8971 return;
8973 if (fc.IsDefinitelyAssigned (variable_info))
8974 return;
8976 fc.Report.Error (188, loc, "The `this' object cannot be used before all of its fields are assigned to");
8979 protected virtual void Error_ThisNotAvailable (ResolveContext ec)
8981 if (ec.IsStatic && !ec.HasSet (ResolveContext.Options.ConstantScope)) {
8982 ec.Report.Error (26, loc, "Keyword `this' is not valid in a static property, static method, or static field initializer");
8983 } else if (ec.CurrentAnonymousMethod != null) {
8984 ec.Report.Error (1673, loc,
8985 "Anonymous methods inside structs cannot access instance members of `this'. " +
8986 "Consider copying `this' to a local variable outside the anonymous method and using the local instead");
8987 } else {
8988 ec.Report.Error (27, loc, "Keyword `this' is not available in the current context");
8992 public override void FlowAnalysis (FlowAnalysisContext fc)
8994 CheckStructThisDefiniteAssignment (fc);
8997 public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
8999 if (ae == null)
9000 return null;
9002 AnonymousMethodStorey storey = ae.Storey;
9003 return storey != null ? storey.HoistedThis : null;
9006 public static bool IsThisAvailable (ResolveContext ec, bool ignoreAnonymous)
9008 if (ec.IsStatic || ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.BaseInitializer | ResolveContext.Options.ConstantScope))
9009 return false;
9011 if (ignoreAnonymous || ec.CurrentAnonymousMethod == null)
9012 return true;
9014 if (ec.CurrentType.IsStruct && !(ec.CurrentAnonymousMethod is StateMachineInitializer))
9015 return false;
9017 return true;
9020 public virtual void ResolveBase (ResolveContext ec)
9022 eclass = ExprClass.Variable;
9023 type = ec.CurrentType;
9025 if (!IsThisAvailable (ec, false)) {
9026 Error_ThisNotAvailable (ec);
9027 return;
9030 var block = ec.CurrentBlock;
9031 if (block != null) {
9032 var top = block.ParametersBlock.TopBlock;
9033 if (top.ThisVariable != null)
9034 variable_info = top.ThisVariable.VariableInfo;
9036 AnonymousExpression am = ec.CurrentAnonymousMethod;
9037 if (am != null && ec.IsVariableCapturingRequired && !block.Explicit.HasCapturedThis) {
9039 // Hoisted this is almost like hoisted variable but not exactly. When
9040 // there is no variable hoisted we can simply emit an instance method
9041 // without lifting this into a storey. Unfotunatelly this complicates
9042 // things in other cases because we don't know where this will be hoisted
9043 // until top-level block is fully resolved
9045 top.AddThisReferenceFromChildrenBlock (block.Explicit);
9046 am.SetHasThisAccess ();
9051 protected override Expression DoResolve (ResolveContext ec)
9053 ResolveBase (ec);
9054 return this;
9057 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
9059 if (eclass == ExprClass.Unresolved)
9060 ResolveBase (ec);
9062 if (type.IsClass || (type.IsReadOnly && !ec.HasSet (ResolveContext.Options.ConstructorScope))) {
9063 if (right_side == EmptyExpression.UnaryAddress)
9064 ec.Report.Error (459, loc, "Cannot take the address of `this' because it is read-only");
9065 else if (right_side == EmptyExpression.OutAccess)
9066 ec.Report.Error (1605, loc, "Cannot pass `this' as a ref or out argument because it is read-only");
9067 else
9068 ec.Report.Error (1604, loc, "Cannot assign to `this' because it is read-only");
9071 return this;
9074 public override int GetHashCode()
9076 throw new NotImplementedException ();
9079 public override bool Equals (object obj)
9081 This t = obj as This;
9082 if (t == null)
9083 return false;
9085 return true;
9088 protected override void CloneTo (CloneContext clonectx, Expression t)
9090 // Nothing
9093 public override void SetHasAddressTaken ()
9095 // Nothing
9098 public override object Accept (StructuralVisitor visitor)
9100 return visitor.Visit (this);
9104 /// <summary>
9105 /// Represents the `__arglist' construct
9106 /// </summary>
9107 public class ArglistAccess : Expression
9109 public ArglistAccess (Location loc)
9111 this.loc = loc;
9114 protected override void CloneTo (CloneContext clonectx, Expression target)
9116 // nothing.
9119 public override bool ContainsEmitWithAwait ()
9121 return false;
9124 public override Expression CreateExpressionTree (ResolveContext ec)
9126 throw new NotSupportedException ("ET");
9129 protected override Expression DoResolve (ResolveContext ec)
9131 eclass = ExprClass.Variable;
9132 type = ec.Module.PredefinedTypes.RuntimeArgumentHandle.Resolve ();
9134 if (ec.HasSet (ResolveContext.Options.FieldInitializerScope) || !ec.CurrentBlock.ParametersBlock.Parameters.HasArglist) {
9135 ec.Report.Error (190, loc,
9136 "The __arglist construct is valid only within a variable argument method");
9139 return this;
9142 public override void Emit (EmitContext ec)
9144 ec.Emit (OpCodes.Arglist);
9147 public override object Accept (StructuralVisitor visitor)
9149 return visitor.Visit (this);
9153 /// <summary>
9154 /// Represents the `__arglist (....)' construct
9155 /// </summary>
9156 public class Arglist : Expression
9158 Arguments arguments;
9160 public Arglist (Location loc)
9161 : this (null, loc)
9165 public Arglist (Arguments args, Location l)
9167 arguments = args;
9168 loc = l;
9171 public Arguments Arguments {
9172 get {
9173 return arguments;
9177 public MetaType[] ArgumentTypes {
9178 get {
9179 if (arguments == null)
9180 return MetaType.EmptyTypes;
9182 var retval = new MetaType[arguments.Count];
9183 for (int i = 0; i < retval.Length; i++)
9184 retval[i] = arguments[i].Expr.Type.GetMetaInfo ();
9186 return retval;
9190 public override bool ContainsEmitWithAwait ()
9192 throw new NotImplementedException ();
9195 public override Expression CreateExpressionTree (ResolveContext ec)
9197 ec.Report.Error (1952, loc, "An expression tree cannot contain a method with variable arguments");
9198 return null;
9201 protected override Expression DoResolve (ResolveContext ec)
9203 eclass = ExprClass.Variable;
9204 type = InternalType.Arglist;
9205 if (arguments != null) {
9206 bool dynamic; // Can be ignored as there is always only 1 overload
9207 arguments.Resolve (ec, out dynamic);
9210 return this;
9213 public override void Emit (EmitContext ec)
9215 if (arguments != null)
9216 arguments.Emit (ec);
9219 protected override void CloneTo (CloneContext clonectx, Expression t)
9221 Arglist target = (Arglist) t;
9223 if (arguments != null)
9224 target.arguments = arguments.Clone (clonectx);
9227 public override object Accept (StructuralVisitor visitor)
9229 return visitor.Visit (this);
9233 public class RefValueExpr : ShimExpression, IAssignMethod, IMemoryLocation
9235 FullNamedExpression texpr;
9237 public RefValueExpr (Expression expr, FullNamedExpression texpr, Location loc)
9238 : base (expr)
9240 this.texpr = texpr;
9241 this.loc = loc;
9244 public FullNamedExpression TypeExpression {
9245 get {
9246 return texpr;
9250 public override bool ContainsEmitWithAwait ()
9252 return false;
9255 public void AddressOf (EmitContext ec, AddressOp mode)
9257 expr.Emit (ec);
9258 ec.Emit (OpCodes.Refanyval, type);
9261 protected override Expression DoResolve (ResolveContext rc)
9263 expr = expr.Resolve (rc);
9264 type = texpr.ResolveAsType (rc);
9265 if (expr == null || type == null)
9266 return null;
9268 expr = Convert.ImplicitConversionRequired (rc, expr, rc.Module.PredefinedTypes.TypedReference.Resolve (), loc);
9269 eclass = ExprClass.Variable;
9270 return this;
9273 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
9275 return DoResolve (rc);
9278 public override void Emit (EmitContext ec)
9280 expr.Emit (ec);
9281 ec.Emit (OpCodes.Refanyval, type);
9282 ec.EmitLoadFromPtr (type);
9285 public void Emit (EmitContext ec, bool leave_copy)
9287 throw new NotImplementedException ();
9290 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
9292 expr.Emit (ec);
9293 ec.Emit (OpCodes.Refanyval, type);
9294 source.Emit (ec);
9296 LocalTemporary temporary = null;
9297 if (leave_copy) {
9298 ec.Emit (OpCodes.Dup);
9299 temporary = new LocalTemporary (source.Type);
9300 temporary.Store (ec);
9303 ec.EmitStoreFromPtr (type);
9305 if (temporary != null) {
9306 temporary.Emit (ec);
9307 temporary.Release (ec);
9311 public override object Accept (StructuralVisitor visitor)
9313 return visitor.Visit (this);
9317 public class RefTypeExpr : ShimExpression
9319 public RefTypeExpr (Expression expr, Location loc)
9320 : base (expr)
9322 this.loc = loc;
9325 protected override Expression DoResolve (ResolveContext rc)
9327 expr = expr.Resolve (rc);
9328 if (expr == null)
9329 return null;
9331 expr = Convert.ImplicitConversionRequired (rc, expr, rc.Module.PredefinedTypes.TypedReference.Resolve (), loc);
9332 if (expr == null)
9333 return null;
9335 type = rc.BuiltinTypes.Type;
9336 eclass = ExprClass.Value;
9337 return this;
9340 public override void Emit (EmitContext ec)
9342 expr.Emit (ec);
9343 ec.Emit (OpCodes.Refanytype);
9344 var m = ec.Module.PredefinedMembers.TypeGetTypeFromHandle.Resolve (loc);
9345 if (m != null)
9346 ec.Emit (OpCodes.Call, m);
9349 public override object Accept (StructuralVisitor visitor)
9351 return visitor.Visit (this);
9355 public class MakeRefExpr : ShimExpression
9357 public MakeRefExpr (Expression expr, Location loc)
9358 : base (expr)
9360 this.loc = loc;
9363 public override bool ContainsEmitWithAwait ()
9365 throw new NotImplementedException ();
9368 protected override Expression DoResolve (ResolveContext rc)
9370 expr = expr.ResolveLValue (rc, EmptyExpression.LValueMemberAccess);
9371 type = rc.Module.PredefinedTypes.TypedReference.Resolve ();
9372 eclass = ExprClass.Value;
9373 return this;
9376 public override void Emit (EmitContext ec)
9378 ((IMemoryLocation) expr).AddressOf (ec, AddressOp.Load);
9379 ec.Emit (OpCodes.Mkrefany, expr.Type);
9382 public override object Accept (StructuralVisitor visitor)
9384 return visitor.Visit (this);
9388 /// <summary>
9389 /// Implements the typeof operator
9390 /// </summary>
9391 public class TypeOf : Expression {
9392 FullNamedExpression QueriedType;
9393 TypeSpec typearg;
9395 public TypeOf (FullNamedExpression queried_type, Location l)
9397 QueriedType = queried_type;
9398 loc = l;
9402 // Use this constructor for any compiler generated typeof expression
9404 public TypeOf (TypeSpec type, Location loc)
9406 this.typearg = type;
9407 this.loc = loc;
9410 #region Properties
9412 public override bool IsSideEffectFree {
9413 get {
9414 return true;
9418 public TypeSpec TypeArgument {
9419 get {
9420 return typearg;
9424 public FullNamedExpression TypeExpression {
9425 get {
9426 return QueriedType;
9430 #endregion
9433 protected override void CloneTo (CloneContext clonectx, Expression t)
9435 TypeOf target = (TypeOf) t;
9436 if (QueriedType != null)
9437 target.QueriedType = (FullNamedExpression) QueriedType.Clone (clonectx);
9440 public override bool ContainsEmitWithAwait ()
9442 return false;
9445 public override Expression CreateExpressionTree (ResolveContext ec)
9447 Arguments args = new Arguments (2);
9448 args.Add (new Argument (this));
9449 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
9450 return CreateExpressionFactoryCall (ec, "Constant", args);
9453 protected override Expression DoResolve (ResolveContext ec)
9455 if (eclass != ExprClass.Unresolved)
9456 return this;
9458 if (typearg == null) {
9460 // Pointer types are allowed without explicit unsafe, they are just tokens
9462 using (ec.Set (ResolveContext.Options.UnsafeScope)) {
9463 typearg = QueriedType.ResolveAsType (ec, true);
9466 if (typearg == null)
9467 return null;
9469 if (typearg.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
9470 ec.Report.Error (1962, QueriedType.Location,
9471 "The typeof operator cannot be used on the dynamic type");
9475 type = ec.BuiltinTypes.Type;
9477 // Even though what is returned is a type object, it's treated as a value by the compiler.
9478 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
9479 eclass = ExprClass.Value;
9480 return this;
9483 static bool ContainsDynamicType (TypeSpec type)
9485 if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
9486 return true;
9488 var element_container = type as ElementTypeSpec;
9489 if (element_container != null)
9490 return ContainsDynamicType (element_container.Element);
9492 foreach (var t in type.TypeArguments) {
9493 if (ContainsDynamicType (t)) {
9494 return true;
9498 return false;
9501 public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType)
9503 // Target type is not System.Type therefore must be object
9504 // and we need to use different encoding sequence
9505 if (targetType != type)
9506 enc.Encode (type);
9508 if (typearg is InflatedTypeSpec) {
9509 var gt = typearg;
9510 do {
9511 if (InflatedTypeSpec.ContainsTypeParameter (gt)) {
9512 rc.Module.Compiler.Report.Error (416, loc, "`{0}': an attribute argument cannot use type parameters",
9513 typearg.GetSignatureForError ());
9514 return;
9517 gt = gt.DeclaringType;
9518 } while (gt != null);
9521 if (ContainsDynamicType (typearg)) {
9522 Attribute.Error_AttributeArgumentIsDynamic (rc, loc);
9523 return;
9526 enc.EncodeTypeName (typearg);
9529 public override void Emit (EmitContext ec)
9531 ec.Emit (OpCodes.Ldtoken, typearg);
9532 var m = ec.Module.PredefinedMembers.TypeGetTypeFromHandle.Resolve (loc);
9533 if (m != null)
9534 ec.Emit (OpCodes.Call, m);
9537 public override object Accept (StructuralVisitor visitor)
9539 return visitor.Visit (this);
9543 sealed class TypeOfMethod : TypeOfMember<MethodSpec>
9545 public TypeOfMethod (MethodSpec method, Location loc)
9546 : base (method, loc)
9550 protected override Expression DoResolve (ResolveContext ec)
9552 if (member.IsConstructor) {
9553 type = ec.Module.PredefinedTypes.ConstructorInfo.Resolve ();
9554 } else {
9555 type = ec.Module.PredefinedTypes.MethodInfo.Resolve ();
9558 if (type == null)
9559 return null;
9561 return base.DoResolve (ec);
9564 public override void Emit (EmitContext ec)
9566 ec.Emit (OpCodes.Ldtoken, member);
9568 base.Emit (ec);
9569 ec.Emit (OpCodes.Castclass, type);
9572 protected override PredefinedMember<MethodSpec> GetTypeFromHandle (EmitContext ec)
9574 return ec.Module.PredefinedMembers.MethodInfoGetMethodFromHandle;
9577 protected override PredefinedMember<MethodSpec> GetTypeFromHandleGeneric (EmitContext ec)
9579 return ec.Module.PredefinedMembers.MethodInfoGetMethodFromHandle2;
9583 abstract class TypeOfMember<T> : Expression where T : MemberSpec
9585 protected readonly T member;
9587 protected TypeOfMember (T member, Location loc)
9589 this.member = member;
9590 this.loc = loc;
9593 public override bool IsSideEffectFree {
9594 get {
9595 return true;
9599 public override bool ContainsEmitWithAwait ()
9601 return false;
9604 public override Expression CreateExpressionTree (ResolveContext ec)
9606 Arguments args = new Arguments (2);
9607 args.Add (new Argument (this));
9608 args.Add (new Argument (new TypeOf (type, loc)));
9609 return CreateExpressionFactoryCall (ec, "Constant", args);
9612 protected override Expression DoResolve (ResolveContext ec)
9614 eclass = ExprClass.Value;
9615 return this;
9618 public override void Emit (EmitContext ec)
9620 bool is_generic = member.DeclaringType.IsGenericOrParentIsGeneric;
9621 PredefinedMember<MethodSpec> p;
9622 if (is_generic) {
9623 p = GetTypeFromHandleGeneric (ec);
9624 ec.Emit (OpCodes.Ldtoken, member.DeclaringType);
9625 } else {
9626 p = GetTypeFromHandle (ec);
9629 var mi = p.Resolve (loc);
9630 if (mi != null)
9631 ec.Emit (OpCodes.Call, mi);
9634 protected abstract PredefinedMember<MethodSpec> GetTypeFromHandle (EmitContext ec);
9635 protected abstract PredefinedMember<MethodSpec> GetTypeFromHandleGeneric (EmitContext ec);
9638 sealed class TypeOfField : TypeOfMember<FieldSpec>
9640 public TypeOfField (FieldSpec field, Location loc)
9641 : base (field, loc)
9645 protected override Expression DoResolve (ResolveContext ec)
9647 type = ec.Module.PredefinedTypes.FieldInfo.Resolve ();
9648 if (type == null)
9649 return null;
9651 return base.DoResolve (ec);
9654 public override void Emit (EmitContext ec)
9656 ec.Emit (OpCodes.Ldtoken, member);
9657 base.Emit (ec);
9660 protected override PredefinedMember<MethodSpec> GetTypeFromHandle (EmitContext ec)
9662 return ec.Module.PredefinedMembers.FieldInfoGetFieldFromHandle;
9665 protected override PredefinedMember<MethodSpec> GetTypeFromHandleGeneric (EmitContext ec)
9667 return ec.Module.PredefinedMembers.FieldInfoGetFieldFromHandle2;
9671 /// <summary>
9672 /// Implements the sizeof expression
9673 /// </summary>
9674 public class SizeOf : Expression {
9675 readonly Expression texpr;
9676 TypeSpec type_queried;
9678 public SizeOf (Expression queried_type, Location l)
9680 this.texpr = queried_type;
9681 loc = l;
9684 public override bool IsSideEffectFree {
9685 get {
9686 return true;
9690 public Expression TypeExpression {
9691 get {
9692 return texpr;
9696 public override bool ContainsEmitWithAwait ()
9698 return false;
9701 public override Expression CreateExpressionTree (ResolveContext ec)
9703 Error_PointerInsideExpressionTree (ec);
9704 return null;
9707 protected override Expression DoResolve (ResolveContext ec)
9709 type_queried = texpr.ResolveAsType (ec);
9710 if (type_queried == null)
9711 return null;
9713 if (type_queried.IsEnum)
9714 type_queried = EnumSpec.GetUnderlyingType (type_queried);
9716 int size_of = BuiltinTypeSpec.GetSize (type_queried);
9717 if (size_of > 0) {
9718 return new IntConstant (ec.BuiltinTypes, size_of, loc);
9721 if (!TypeManager.VerifyUnmanaged (ec.Module, type_queried, loc)){
9722 return null;
9725 if (!ec.IsUnsafe) {
9726 ec.Report.Error (233, loc,
9727 "`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)",
9728 type_queried.GetSignatureForError ());
9731 type = ec.BuiltinTypes.Int;
9732 eclass = ExprClass.Value;
9733 return this;
9736 public override void Emit (EmitContext ec)
9738 ec.Emit (OpCodes.Sizeof, type_queried);
9741 protected override void CloneTo (CloneContext clonectx, Expression t)
9745 public override object Accept (StructuralVisitor visitor)
9747 return visitor.Visit (this);
9751 /// <summary>
9752 /// Implements the qualified-alias-member (::) expression.
9753 /// </summary>
9754 public class QualifiedAliasMember : MemberAccess
9756 readonly string alias;
9757 public static readonly string GlobalAlias = "global";
9759 public QualifiedAliasMember (string alias, string identifier, Location l)
9760 : base (null, identifier, l)
9762 this.alias = alias;
9765 public QualifiedAliasMember (string alias, string identifier, TypeArguments targs, Location l)
9766 : base (null, identifier, targs, l)
9768 this.alias = alias;
9771 public QualifiedAliasMember (string alias, string identifier, int arity, Location l)
9772 : base (null, identifier, arity, l)
9774 this.alias = alias;
9777 public string Alias {
9778 get {
9779 return alias;
9783 public FullNamedExpression CreateExpressionFromAlias (IMemberContext mc)
9785 if (alias == GlobalAlias)
9786 return new NamespaceExpression (mc.Module.GlobalRootNamespace, loc);
9788 int errors = mc.Module.Compiler.Report.Errors;
9789 var expr = mc.LookupNamespaceAlias (alias);
9790 if (expr == null) {
9791 if (errors == mc.Module.Compiler.Report.Errors)
9792 mc.Module.Compiler.Report.Error (432, loc, "Alias `{0}' not found", alias);
9794 return null;
9797 return expr;
9800 public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments)
9802 expr = CreateExpressionFromAlias (mc);
9803 if (expr == null)
9804 return null;
9806 return base.ResolveAsTypeOrNamespace (mc, allowUnboundTypeArguments);
9809 protected override Expression DoResolve (ResolveContext rc)
9811 return ResolveAsTypeOrNamespace (rc, false);
9814 public override string GetSignatureForError ()
9816 string name = Name;
9817 if (targs != null) {
9818 name = Name + "<" + targs.GetSignatureForError () + ">";
9821 return alias + "::" + name;
9824 public override bool HasConditionalAccess ()
9826 return false;
9829 public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions)
9831 if ((restrictions & MemberLookupRestrictions.InvocableOnly) != 0) {
9832 rc.Module.Compiler.Report.Error (687, loc,
9833 "The namespace alias qualifier `::' cannot be used to invoke a method. Consider using `.' instead",
9834 GetSignatureForError ());
9836 return null;
9839 return DoResolve (rc);
9842 protected override void CloneTo (CloneContext clonectx, Expression t)
9844 // Nothing
9847 public override object Accept (StructuralVisitor visitor)
9849 return visitor.Visit (this);
9853 /// <summary>
9854 /// Implements the member access expression
9855 /// </summary>
9856 public class MemberAccess : ATypeNameExpression
9858 protected Expression expr;
9860 public MemberAccess (Expression expr, string id)
9861 : base (id, expr.Location)
9863 this.expr = expr;
9866 public MemberAccess (Expression expr, string identifier, Location loc)
9867 : base (identifier, loc)
9869 this.expr = expr;
9872 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
9873 : base (identifier, args, loc)
9875 this.expr = expr;
9878 public MemberAccess (Expression expr, string identifier, int arity, Location loc)
9879 : base (identifier, arity, loc)
9881 this.expr = expr;
9884 public Expression LeftExpression {
9885 get {
9886 return expr;
9890 public override Location StartLocation {
9891 get {
9892 return expr == null ? loc : expr.StartLocation;
9896 protected override Expression DoResolve (ResolveContext rc)
9898 var e = LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.DontSetConditionalAccess);
9899 if (e != null)
9900 e = e.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.Type | ResolveFlags.MethodGroup);
9902 return e;
9905 public override Expression DoResolveLValue (ResolveContext rc, Expression rhs)
9907 var e = LookupNameExpression (rc, MemberLookupRestrictions.None);
9909 if (e is TypeExpr) {
9910 e.Error_UnexpectedKind (rc, ResolveFlags.VariableOrValue, loc);
9911 return null;
9914 if (e != null)
9915 e = e.ResolveLValue (rc, rhs);
9917 return e;
9920 protected virtual void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type)
9922 if (type == InternalType.NullLiteral && rc.IsRuntimeBinder)
9923 rc.Report.Error (Report.RuntimeErrorId, loc, "Cannot perform member binding on `null' value");
9924 else
9925 expr.Error_OperatorCannotBeApplied (rc, loc, ".", type);
9928 public override bool HasConditionalAccess ()
9930 return LeftExpression.HasConditionalAccess ();
9933 public static bool IsValidDotExpression (TypeSpec type)
9935 const MemberKind dot_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.Delegate | MemberKind.Enum |
9936 MemberKind.Interface | MemberKind.TypeParameter | MemberKind.ArrayType | MemberKind.ByRef;
9938 return (type.Kind & dot_kinds) != 0 || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
9941 public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions)
9943 var sn = expr as SimpleName;
9944 const ResolveFlags flags = ResolveFlags.VariableOrValue | ResolveFlags.Type;
9946 if (sn != null) {
9947 expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity);
9950 // Resolve expression which does have type set as we need expression type
9951 // with disable flow analysis as we don't know whether left side expression
9952 // is used as variable or type
9954 if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess || expr is EventExpr) {
9955 expr = expr.Resolve (rc);
9956 } else if (expr is TypeParameterExpr) {
9957 expr.Error_UnexpectedKind (rc, flags, sn.Location);
9958 expr = null;
9960 } else {
9961 if ((restrictions & MemberLookupRestrictions.DontSetConditionalAccess) != 0) {
9962 using (rc.Set (ResolveContext.Options.DontSetConditionalAccessReceiver)) {
9963 expr = expr.Resolve (rc, flags);
9965 } else {
9966 expr = expr.Resolve (rc, flags);
9970 if (expr == null)
9971 return null;
9973 var ns = expr as NamespaceExpression;
9974 if (ns != null) {
9975 var retval = ns.LookupTypeOrNamespace (rc, Name, Arity, LookupMode.Normal, loc);
9977 if (retval == null) {
9978 ns.Error_NamespaceDoesNotExist (rc, Name, Arity, loc);
9979 return null;
9982 if (Arity > 0) {
9983 if (HasTypeArguments)
9984 return new GenericTypeExpr (retval.Type, targs, loc);
9986 targs.Resolve (rc, false);
9989 return retval;
9992 var cma = this as ConditionalMemberAccess;
9994 MemberExpr me;
9995 TypeSpec expr_type = expr.Type;
9996 if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
9997 me = expr as MemberExpr;
9998 if (me != null)
9999 me.ResolveInstanceExpression (rc, null);
10001 Arguments args = new Arguments (1);
10002 args.Add (new Argument (expr));
10004 if (cma != null)
10005 return new DynamicConditionalMemberBinder (Name, args, loc);
10007 return new DynamicMemberBinder (Name, args, loc);
10010 if (cma != null) {
10011 if (!IsNullPropagatingValid (expr.Type)) {
10012 expr.Error_OperatorCannotBeApplied (rc, loc, "?", expr.Type);
10013 return null;
10016 if (expr_type.IsNullableType) {
10017 expr = Nullable.Unwrap.Create (expr.Resolve (rc), true);
10018 expr_type = expr.Type;
10022 if (!IsValidDotExpression (expr_type)) {
10023 Error_OperatorCannotBeApplied (rc, expr_type);
10024 return null;
10027 var lookup_arity = Arity;
10028 bool errorMode = false;
10029 Expression member_lookup;
10030 while (true) {
10031 member_lookup = MemberLookup (rc, errorMode, expr_type, Name, lookup_arity, restrictions, loc);
10032 if (member_lookup == null) {
10034 // Try to look for extension method when member lookup failed
10036 if (MethodGroupExpr.IsExtensionMethodArgument (expr)) {
10037 var methods = rc.LookupExtensionMethod (Name, lookup_arity);
10038 if (methods != null) {
10039 var emg = new ExtensionMethodGroupExpr (methods, expr, loc);
10040 if (HasTypeArguments) {
10041 if (!targs.Resolve (rc, false))
10042 return null;
10044 emg.SetTypeArguments (rc, targs);
10047 if (cma != null)
10048 emg.ConditionalAccess = true;
10050 // TODO: it should really skip the checks bellow
10051 return emg.Resolve (rc);
10056 if (errorMode) {
10057 if (member_lookup == null) {
10058 var dep = expr_type.GetMissingDependencies ();
10059 if (dep != null) {
10060 ImportedTypeDefinition.Error_MissingDependency (rc, dep, loc);
10061 } else if (expr is TypeExpr) {
10062 base.Error_TypeDoesNotContainDefinition (rc, expr_type, Name);
10063 } else {
10064 Error_TypeDoesNotContainDefinition (rc, expr_type, Name);
10067 return null;
10070 if (member_lookup is MethodGroupExpr || member_lookup is PropertyExpr) {
10071 // Leave it to overload resolution to report correct error
10072 } else if (!(member_lookup is TypeExpr)) {
10073 // TODO: rc.SymbolRelatedToPreviousError
10074 ErrorIsInaccesible (rc, member_lookup.GetSignatureForError (), loc);
10076 break;
10079 if (member_lookup != null)
10080 break;
10082 lookup_arity = 0;
10083 restrictions &= ~MemberLookupRestrictions.InvocableOnly;
10084 errorMode = true;
10087 TypeExpr texpr = member_lookup as TypeExpr;
10088 if (texpr != null) {
10089 if (!(expr is TypeExpr) && (sn == null || expr.ProbeIdenticalTypeName (rc, expr, sn) == expr)) {
10090 rc.Report.Error (572, loc, "`{0}': cannot reference a type through an expression. Consider using `{1}' instead",
10091 Name, texpr.GetSignatureForError ());
10094 if (!texpr.Type.IsAccessible (rc)) {
10095 rc.Report.SymbolRelatedToPreviousError (member_lookup.Type);
10096 ErrorIsInaccesible (rc, member_lookup.Type.GetSignatureForError (), loc);
10097 return null;
10100 if (HasTypeArguments) {
10101 return new GenericTypeExpr (member_lookup.Type, targs, loc);
10104 return member_lookup;
10107 me = member_lookup as MemberExpr;
10109 if (sn != null && me.IsStatic && (expr = me.ProbeIdenticalTypeName (rc, expr, sn)) != expr) {
10110 sn = null;
10113 if (cma != null) {
10114 me.ConditionalAccess = true;
10117 me = me.ResolveMemberAccess (rc, expr, sn);
10119 if (Arity > 0) {
10120 if (!targs.Resolve (rc, false))
10121 return null;
10123 me.SetTypeArguments (rc, targs);
10126 return me;
10129 public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext rc, bool allowUnboundTypeArguments)
10131 FullNamedExpression fexpr = expr as FullNamedExpression;
10132 if (fexpr == null) {
10133 expr.ResolveAsType (rc);
10134 return null;
10137 FullNamedExpression expr_resolved = fexpr.ResolveAsTypeOrNamespace (rc, allowUnboundTypeArguments);
10139 if (expr_resolved == null)
10140 return null;
10142 var ns = expr_resolved as NamespaceExpression;
10143 if (ns != null) {
10144 FullNamedExpression retval = ns.LookupTypeOrNamespace (rc, Name, Arity, LookupMode.Normal, loc);
10146 if (retval == null) {
10147 ns.Error_NamespaceDoesNotExist (rc, Name, Arity, loc);
10148 } else if (Arity > 0) {
10149 if (HasTypeArguments) {
10150 retval = new GenericTypeExpr (retval.Type, targs, loc);
10151 if (retval.ResolveAsType (rc) == null)
10152 return null;
10153 } else {
10154 targs.Resolve (rc, allowUnboundTypeArguments);
10156 retval = new GenericOpenTypeExpr (retval.Type, loc);
10160 return retval;
10163 var tnew_expr = expr_resolved.ResolveAsType (rc);
10164 if (tnew_expr == null)
10165 return null;
10167 TypeSpec expr_type = tnew_expr;
10168 if (TypeManager.IsGenericParameter (expr_type)) {
10169 rc.Module.Compiler.Report.Error (704, loc, "A nested type cannot be specified through a type parameter `{0}'",
10170 tnew_expr.GetSignatureForError ());
10171 return null;
10174 var qam = this as QualifiedAliasMember;
10175 if (qam != null) {
10176 rc.Module.Compiler.Report.Error (431, loc,
10177 "Alias `{0}' cannot be used with `::' since it denotes a type. Consider replacing `::' with `.'",
10178 qam.Alias);
10182 TypeSpec nested = null;
10183 while (expr_type != null) {
10184 nested = MemberCache.FindNestedType (expr_type, Name, Arity, false);
10185 if (nested == null) {
10186 if (expr_type == tnew_expr) {
10187 Error_IdentifierNotFound (rc, expr_type);
10188 return null;
10191 expr_type = tnew_expr;
10192 nested = MemberCache.FindNestedType (expr_type, Name, Arity, false);
10193 ErrorIsInaccesible (rc, nested.GetSignatureForError (), loc);
10194 break;
10197 if (nested.IsAccessible (rc))
10198 break;
10201 // Keep looking after inaccessible candidate but only if
10202 // we are not in same context as the definition itself
10204 if (expr_type.MemberDefinition == rc.CurrentMemberDefinition)
10205 break;
10207 expr_type = expr_type.BaseType;
10210 TypeExpr texpr;
10211 if (Arity > 0) {
10212 if (HasTypeArguments) {
10213 texpr = new GenericTypeExpr (nested, targs, loc);
10214 } else {
10215 targs.Resolve (rc, allowUnboundTypeArguments && !(expr_resolved is GenericTypeExpr));
10217 texpr = new GenericOpenTypeExpr (nested, loc);
10219 } else if (expr_resolved is GenericOpenTypeExpr) {
10220 texpr = new GenericOpenTypeExpr (nested, loc);
10221 } else {
10222 texpr = new TypeExpression (nested, loc);
10225 if (texpr.ResolveAsType (rc) == null)
10226 return null;
10228 return texpr;
10231 public void Error_IdentifierNotFound (IMemberContext rc, TypeSpec expr_type)
10233 var nested = MemberCache.FindNestedType (expr_type, Name, -System.Math.Max (1, Arity), false);
10235 if (nested != null) {
10236 Error_TypeArgumentsCannotBeUsed (rc, nested, expr.Location);
10237 return;
10240 var any_other_member = MemberLookup (rc, false, expr_type, Name, 0, MemberLookupRestrictions.None, loc);
10241 if (any_other_member != null) {
10242 Error_UnexpectedKind (rc, any_other_member, "type", any_other_member.ExprClassName, loc);
10243 return;
10246 rc.Module.Compiler.Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
10247 Name, expr_type.GetSignatureForError ());
10250 protected override void Error_InvalidExpressionStatement (Report report, Location loc)
10252 base.Error_InvalidExpressionStatement (report, LeftExpression.Location);
10255 public override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name)
10257 if (ec.Module.Compiler.Settings.Version > LanguageVersion.ISO_2 && !ec.IsRuntimeBinder && MethodGroupExpr.IsExtensionMethodArgument (expr)) {
10258 ec.Report.SymbolRelatedToPreviousError (type);
10260 var cand = ec.Module.GlobalRootNamespace.FindExtensionMethodNamespaces (ec, name, Arity);
10261 string missing;
10262 // a using directive or an assembly reference
10263 if (cand != null) {
10264 missing = "`" + string.Join ("' or `", cand.ToArray ()) + "' using directive";
10265 } else {
10266 missing = "an assembly reference";
10269 ec.Report.Error (1061, loc,
10270 "Type `{0}' does not contain a definition for `{1}' and no extension method `{1}' of type `{0}' could be found. Are you missing {2}?",
10271 type.GetSignatureForError (), name, missing);
10272 return;
10275 base.Error_TypeDoesNotContainDefinition (ec, type, name);
10278 public override string GetSignatureForError ()
10280 return expr.GetSignatureForError () + "." + base.GetSignatureForError ();
10283 protected override void CloneTo (CloneContext clonectx, Expression t)
10285 MemberAccess target = (MemberAccess) t;
10287 target.expr = expr.Clone (clonectx);
10290 public override object Accept (StructuralVisitor visitor)
10292 return visitor.Visit (this);
10296 public class ConditionalMemberAccess : MemberAccess
10298 public ConditionalMemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
10299 : base (expr, identifier, args, loc)
10303 public override bool HasConditionalAccess ()
10305 return true;
10309 /// <summary>
10310 /// Implements checked expressions
10311 /// </summary>
10312 public class CheckedExpr : Expression {
10314 public Expression Expr;
10316 public CheckedExpr (Expression e, Location l)
10318 Expr = e;
10319 loc = l;
10322 public override bool ContainsEmitWithAwait ()
10324 return Expr.ContainsEmitWithAwait ();
10327 public override Expression CreateExpressionTree (ResolveContext ec)
10329 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
10330 return Expr.CreateExpressionTree (ec);
10333 protected override Expression DoResolve (ResolveContext ec)
10335 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
10336 Expr = Expr.Resolve (ec);
10338 if (Expr == null)
10339 return null;
10341 if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression || Expr is DefaultValueExpression)
10342 return Expr;
10344 eclass = Expr.eclass;
10345 type = Expr.Type;
10346 return this;
10349 public override void Emit (EmitContext ec)
10351 using (ec.With (EmitContext.Options.CheckedScope, true))
10352 Expr.Emit (ec);
10355 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
10357 using (ec.With (EmitContext.Options.CheckedScope, true))
10358 Expr.EmitBranchable (ec, target, on_true);
10361 public override void FlowAnalysis (FlowAnalysisContext fc)
10363 Expr.FlowAnalysis (fc);
10366 public override SLE.Expression MakeExpression (BuilderContext ctx)
10368 using (ctx.With (BuilderContext.Options.CheckedScope, true)) {
10369 return Expr.MakeExpression (ctx);
10373 protected override void CloneTo (CloneContext clonectx, Expression t)
10375 CheckedExpr target = (CheckedExpr) t;
10377 target.Expr = Expr.Clone (clonectx);
10380 public override object Accept (StructuralVisitor visitor)
10382 return visitor.Visit (this);
10386 /// <summary>
10387 /// Implements the unchecked expression
10388 /// </summary>
10389 public class UnCheckedExpr : Expression {
10391 public Expression Expr;
10393 public UnCheckedExpr (Expression e, Location l)
10395 Expr = e;
10396 loc = l;
10399 public override bool ContainsEmitWithAwait ()
10401 return Expr.ContainsEmitWithAwait ();
10404 public override Expression CreateExpressionTree (ResolveContext ec)
10406 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
10407 return Expr.CreateExpressionTree (ec);
10410 protected override Expression DoResolve (ResolveContext ec)
10412 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
10413 Expr = Expr.Resolve (ec);
10415 if (Expr == null)
10416 return null;
10418 if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression || Expr is DefaultValueExpression)
10419 return Expr;
10421 eclass = Expr.eclass;
10422 type = Expr.Type;
10423 return this;
10426 public override void Emit (EmitContext ec)
10428 using (ec.With (EmitContext.Options.CheckedScope, false))
10429 Expr.Emit (ec);
10432 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
10434 using (ec.With (EmitContext.Options.CheckedScope, false))
10435 Expr.EmitBranchable (ec, target, on_true);
10438 public override void FlowAnalysis (FlowAnalysisContext fc)
10440 Expr.FlowAnalysis (fc);
10443 protected override void CloneTo (CloneContext clonectx, Expression t)
10445 UnCheckedExpr target = (UnCheckedExpr) t;
10447 target.Expr = Expr.Clone (clonectx);
10450 public override object Accept (StructuralVisitor visitor)
10452 return visitor.Visit (this);
10456 /// <summary>
10457 /// An Element Access expression.
10459 /// During semantic analysis these are transformed into
10460 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
10461 /// </summary>
10462 public class ElementAccess : Expression
10464 public Arguments Arguments;
10465 public Expression Expr;
10466 bool conditional_access_receiver;
10468 public ElementAccess (Expression e, Arguments args, Location loc)
10470 Expr = e;
10471 this.loc = loc;
10472 this.Arguments = args;
10475 public bool ConditionalAccess { get; set; }
10477 public override Location StartLocation {
10478 get {
10479 return Expr.StartLocation;
10483 public override bool ContainsEmitWithAwait ()
10485 return Expr.ContainsEmitWithAwait () || Arguments.ContainsEmitWithAwait ();
10489 // We perform some simple tests, and then to "split" the emit and store
10490 // code we create an instance of a different class, and return that.
10492 Expression CreateAccessExpression (ResolveContext ec, bool conditionalAccessReceiver)
10494 if (conditionalAccessReceiver)
10495 ec.Set (ResolveContext.Options.DontSetConditionalAccessReceiver);
10497 Expr = Expr.Resolve (ec);
10499 if (conditionalAccessReceiver)
10500 ec.With (ResolveContext.Options.DontSetConditionalAccessReceiver, false);
10502 if (Expr == null)
10503 return null;
10505 type = Expr.Type;
10507 if (ConditionalAccess && !IsNullPropagatingValid (type)) {
10508 Error_OperatorCannotBeApplied (ec, loc, "?", type);
10509 return null;
10512 if (type.IsArray) {
10513 var aa = new ArrayAccess (this, loc) {
10514 ConditionalAccess = ConditionalAccess,
10517 if (conditionalAccessReceiver)
10518 aa.SetConditionalAccessReceiver ();
10520 return aa;
10523 if (type.IsPointer)
10524 return Expr.MakePointerAccess (ec, type, Arguments);
10526 FieldExpr fe = Expr as FieldExpr;
10527 if (fe != null) {
10528 var ff = fe.Spec as FixedFieldSpec;
10529 if (ff != null) {
10530 return Expr.MakePointerAccess (ec, ff.ElementType, Arguments);
10534 var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false);
10535 if (indexers != null || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
10536 var indexer = new IndexerExpr (indexers, type, this) {
10537 ConditionalAccess = ConditionalAccess
10540 if (conditionalAccessReceiver)
10541 indexer.SetConditionalAccessReceiver ();
10543 return indexer;
10546 Error_CannotApplyIndexing (ec, type, loc);
10548 return null;
10551 public override Expression CreateExpressionTree (ResolveContext ec)
10553 Arguments args = Arguments.CreateForExpressionTree (ec, Arguments,
10554 Expr.CreateExpressionTree (ec));
10556 return CreateExpressionFactoryCall (ec, "ArrayIndex", args);
10559 public static void Error_CannotApplyIndexing (ResolveContext rc, TypeSpec type, Location loc)
10561 if (type != InternalType.ErrorType) {
10562 rc.Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
10563 type.GetSignatureForError ());
10567 public override bool HasConditionalAccess ()
10569 return ConditionalAccess || Expr.HasConditionalAccess ();
10572 void ResolveConditionalAccessReceiver (ResolveContext rc)
10574 if (!rc.HasSet (ResolveContext.Options.DontSetConditionalAccessReceiver) && HasConditionalAccess ()) {
10575 conditional_access_receiver = true;
10579 protected override Expression DoResolve (ResolveContext rc)
10581 ResolveConditionalAccessReceiver (rc);
10583 var expr = CreateAccessExpression (rc, conditional_access_receiver);
10584 if (expr == null)
10585 return null;
10587 return expr.Resolve (rc);
10590 public override Expression DoResolveLValue (ResolveContext ec, Expression rhs)
10592 var res = CreateAccessExpression (ec, false);
10593 if (res == null)
10594 return null;
10596 return res.ResolveLValue (ec, rhs);
10599 public override void Emit (EmitContext ec)
10601 throw new Exception ("Should never be reached");
10604 public override void FlowAnalysis (FlowAnalysisContext fc)
10606 Expr.FlowAnalysis (fc);
10608 Arguments.FlowAnalysis (fc);
10611 public override string GetSignatureForError ()
10613 return Expr.GetSignatureForError ();
10616 protected override void CloneTo (CloneContext clonectx, Expression t)
10618 ElementAccess target = (ElementAccess) t;
10620 target.Expr = Expr.Clone (clonectx);
10621 if (Arguments != null)
10622 target.Arguments = Arguments.Clone (clonectx);
10625 public override object Accept (StructuralVisitor visitor)
10627 return visitor.Visit (this);
10631 /// <summary>
10632 /// Implements array access
10633 /// </summary>
10634 public class ArrayAccess : Expression, IDynamicAssign, IMemoryLocation {
10636 // Points to our "data" repository
10638 ElementAccess ea;
10640 LocalTemporary temp;
10641 bool prepared;
10642 bool? has_await_args;
10643 bool conditional_access_receiver;
10645 public ArrayAccess (ElementAccess ea_data, Location l)
10647 ea = ea_data;
10648 loc = l;
10651 public bool ConditionalAccess { get; set; }
10653 public void AddressOf (EmitContext ec, AddressOp mode)
10655 var ac = (ArrayContainer) ea.Expr.Type;
10657 if (!has_await_args.HasValue && ec.HasSet (BuilderContext.Options.AsyncBody) && ea.Arguments.ContainsEmitWithAwait ()) {
10658 LoadInstanceAndArguments (ec, false, true);
10661 LoadInstanceAndArguments (ec, false, false);
10663 if (ac.Element.IsGenericParameter && mode == AddressOp.Load)
10664 ec.Emit (OpCodes.Readonly);
10666 ec.EmitArrayAddress (ac);
10669 public override Expression CreateExpressionTree (ResolveContext ec)
10671 if (ConditionalAccess)
10672 Error_NullShortCircuitInsideExpressionTree (ec);
10674 return ea.CreateExpressionTree (ec);
10677 public override bool ContainsEmitWithAwait ()
10679 return ea.ContainsEmitWithAwait ();
10682 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
10684 if (HasConditionalAccess ())
10685 Error_NullPropagatingLValue (ec);
10687 return DoResolve (ec);
10690 protected override Expression DoResolve (ResolveContext ec)
10692 // dynamic is used per argument in ConvertExpressionToArrayIndex case
10693 bool dynamic;
10694 ea.Arguments.Resolve (ec, out dynamic);
10696 var ac = ea.Expr.Type as ArrayContainer;
10697 int rank = ea.Arguments.Count;
10698 if (ac.Rank != rank) {
10699 ec.Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
10700 rank.ToString (), ac.Rank.ToString ());
10701 return null;
10704 type = ac.Element;
10705 if (type.IsPointer) {
10706 if (ec.CurrentIterator != null) {
10707 UnsafeInsideIteratorError (ec, ea.Location);
10708 } else if (!ec.IsUnsafe) {
10709 UnsafeError (ec, ea.Location);
10713 if (conditional_access_receiver)
10714 type = LiftMemberType (ec, type);
10716 foreach (Argument a in ea.Arguments) {
10717 var na = a as NamedArgument;
10718 if (na != null)
10719 ElementAccess.Error_NamedArgument (na, ec.Report);
10721 a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr);
10724 eclass = ExprClass.Variable;
10726 return this;
10729 protected override void Error_NegativeArrayIndex (ResolveContext ec, Location loc)
10731 ec.Report.Warning (251, 2, loc, "Indexing an array with a negative index (array indices always start at zero)");
10734 public override void FlowAnalysis (FlowAnalysisContext fc)
10736 var da = conditional_access_receiver ? fc.BranchDefiniteAssignment () : null;
10738 ea.FlowAnalysis (fc);
10740 if (conditional_access_receiver)
10741 fc.DefiniteAssignment = da;
10744 public override bool HasConditionalAccess ()
10746 return ConditionalAccess || ea.Expr.HasConditionalAccess ();
10750 // Load the array arguments into the stack.
10752 void LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait)
10754 if (prepareAwait) {
10755 ea.Expr = ea.Expr.EmitToField (ec);
10756 } else {
10757 var ie = new InstanceEmitter (ea.Expr, false);
10758 ie.Emit (ec, ConditionalAccess);
10760 if (duplicateArguments) {
10761 ec.Emit (OpCodes.Dup);
10763 var copy = new LocalTemporary (ea.Expr.Type);
10764 copy.Store (ec);
10765 ea.Expr = copy;
10769 var dup_args = ea.Arguments.Emit (ec, duplicateArguments, prepareAwait);
10770 if (dup_args != null)
10771 ea.Arguments = dup_args;
10774 public void Emit (EmitContext ec, bool leave_copy)
10776 if (prepared) {
10777 ec.EmitLoadFromPtr (type);
10778 } else {
10779 if (!has_await_args.HasValue && ec.HasSet (BuilderContext.Options.AsyncBody) && ea.Arguments.ContainsEmitWithAwait ()) {
10780 LoadInstanceAndArguments (ec, false, true);
10783 if (conditional_access_receiver)
10784 ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
10786 var ac = (ArrayContainer) ea.Expr.Type;
10787 LoadInstanceAndArguments (ec, false, false);
10788 ec.EmitArrayLoad (ac);
10790 if (conditional_access_receiver)
10791 ec.CloseConditionalAccess (type.IsNullableType && type != ac.Element ? type : null);
10794 if (leave_copy) {
10795 ec.Emit (OpCodes.Dup);
10796 temp = new LocalTemporary (this.type);
10797 temp.Store (ec);
10801 public override void Emit (EmitContext ec)
10803 Emit (ec, false);
10806 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
10808 var ac = (ArrayContainer) ea.Expr.Type;
10809 TypeSpec t = source.Type;
10811 has_await_args = ec.HasSet (BuilderContext.Options.AsyncBody) && (ea.Arguments.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ());
10814 // When we are dealing with a struct, get the address of it to avoid value copy
10815 // Same cannot be done for reference type because array covariance and the
10816 // check in ldelema requires to specify the type of array element stored at the index
10818 if (t.IsStruct && ((isCompound && !(source is DynamicExpressionStatement)) || !BuiltinTypeSpec.IsPrimitiveType (t))) {
10819 LoadInstanceAndArguments (ec, false, has_await_args.Value);
10821 if (has_await_args.Value) {
10822 if (source.ContainsEmitWithAwait ()) {
10823 source = source.EmitToField (ec);
10824 isCompound = false;
10825 prepared = true;
10828 LoadInstanceAndArguments (ec, isCompound, false);
10829 } else {
10830 prepared = true;
10833 ec.EmitArrayAddress (ac);
10835 if (isCompound) {
10836 ec.Emit (OpCodes.Dup);
10837 prepared = true;
10839 } else {
10840 LoadInstanceAndArguments (ec, isCompound, has_await_args.Value);
10842 if (has_await_args.Value) {
10843 if (source.ContainsEmitWithAwait ())
10844 source = source.EmitToField (ec);
10846 LoadInstanceAndArguments (ec, false, false);
10850 source.Emit (ec);
10852 if (isCompound) {
10853 var lt = ea.Expr as LocalTemporary;
10854 if (lt != null)
10855 lt.Release (ec);
10858 if (leave_copy) {
10859 ec.Emit (OpCodes.Dup);
10860 temp = new LocalTemporary (this.type);
10861 temp.Store (ec);
10864 if (prepared) {
10865 ec.EmitStoreFromPtr (t);
10866 } else {
10867 ec.EmitArrayStore (ac);
10870 if (temp != null) {
10871 temp.Emit (ec);
10872 temp.Release (ec);
10876 public override Expression EmitToField (EmitContext ec)
10879 // Have to be specialized for arrays to get access to
10880 // underlying element. Instead of another result copy we
10881 // need direct access to element
10883 // Consider:
10885 // CallRef (ref a[await Task.Factory.StartNew (() => 1)]);
10887 ea.Expr = ea.Expr.EmitToField (ec);
10888 ea.Arguments = ea.Arguments.Emit (ec, false, true);
10889 return this;
10892 public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source)
10894 return SLE.Expression.ArrayAccess (ea.Expr.MakeExpression (ctx), MakeExpressionArguments (ctx));
10897 public override SLE.Expression MakeExpression (BuilderContext ctx)
10899 return SLE.Expression.ArrayIndex (ea.Expr.MakeExpression (ctx), MakeExpressionArguments (ctx));
10902 SLE.Expression[] MakeExpressionArguments (BuilderContext ctx)
10904 using (ctx.With (BuilderContext.Options.CheckedScope, true)) {
10905 return Arguments.MakeExpression (ea.Arguments, ctx);
10909 public void SetConditionalAccessReceiver ()
10911 conditional_access_receiver = true;
10916 // Indexer access expression
10918 class IndexerExpr : PropertyOrIndexerExpr<IndexerSpec>, OverloadResolver.IBaseMembersProvider
10920 IList<MemberSpec> indexers;
10921 Arguments arguments;
10922 TypeSpec queried_type;
10924 public IndexerExpr (IList<MemberSpec> indexers, TypeSpec queriedType, ElementAccess ea)
10925 : this (indexers, queriedType, ea.Expr, ea.Arguments, ea.Location)
10929 public IndexerExpr (IList<MemberSpec> indexers, TypeSpec queriedType, Expression instance, Arguments args, Location loc)
10930 : base (loc)
10932 this.indexers = indexers;
10933 this.queried_type = queriedType;
10934 this.InstanceExpression = instance;
10935 this.arguments = args;
10938 #region Properties
10940 protected override Arguments Arguments {
10941 get {
10942 return arguments;
10944 set {
10945 arguments = value;
10949 protected override TypeSpec DeclaringType {
10950 get {
10951 return best_candidate.DeclaringType;
10955 public override bool IsInstance {
10956 get {
10957 return true;
10961 public override bool IsStatic {
10962 get {
10963 return false;
10967 public override string KindName {
10968 get { return "indexer"; }
10971 public override string Name {
10972 get {
10973 return "this";
10977 #endregion
10979 public override bool ContainsEmitWithAwait ()
10981 return base.ContainsEmitWithAwait () || arguments.ContainsEmitWithAwait ();
10984 public override Expression CreateExpressionTree (ResolveContext ec)
10986 if (ConditionalAccess) {
10987 Error_NullShortCircuitInsideExpressionTree (ec);
10990 Arguments args = Arguments.CreateForExpressionTree (ec, arguments,
10991 InstanceExpression.CreateExpressionTree (ec),
10992 new TypeOfMethod (Getter, loc));
10994 return CreateExpressionFactoryCall (ec, "Call", args);
10997 public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
10999 LocalTemporary await_source_arg = null;
11001 if (isCompound) {
11002 emitting_compound_assignment = true;
11003 if (source is DynamicExpressionStatement) {
11004 Emit (ec, false);
11005 } else {
11006 source.Emit (ec);
11008 emitting_compound_assignment = false;
11010 if (has_await_arguments) {
11011 await_source_arg = new LocalTemporary (Type);
11012 await_source_arg.Store (ec);
11014 arguments.Add (new Argument (await_source_arg));
11016 if (leave_copy) {
11017 temp = await_source_arg;
11020 has_await_arguments = false;
11021 } else {
11022 arguments = null;
11024 if (leave_copy) {
11025 ec.Emit (OpCodes.Dup);
11026 temp = new LocalTemporary (Type);
11027 temp.Store (ec);
11030 } else {
11031 if (leave_copy) {
11032 if (ec.HasSet (BuilderContext.Options.AsyncBody) && (arguments.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ())) {
11033 source = source.EmitToField (ec);
11034 } else {
11035 temp = new LocalTemporary (Type);
11036 source.Emit (ec);
11037 temp.Store (ec);
11038 source = temp;
11042 arguments.Add (new Argument (source));
11045 var call = new CallEmitter ();
11046 call.InstanceExpression = InstanceExpression;
11047 if (arguments == null)
11048 call.InstanceExpressionOnStack = true;
11050 call.Emit (ec, Setter, arguments, loc);
11052 if (temp != null) {
11053 temp.Emit (ec);
11054 temp.Release (ec);
11055 } else if (leave_copy) {
11056 source.Emit (ec);
11059 if (await_source_arg != null) {
11060 await_source_arg.Release (ec);
11064 public override void FlowAnalysis (FlowAnalysisContext fc)
11066 var da = conditional_access_receiver ? fc.BranchDefiniteAssignment () : null;
11068 base.FlowAnalysis (fc);
11069 arguments.FlowAnalysis (fc);
11071 if (conditional_access_receiver)
11072 fc.DefiniteAssignment = da;
11075 public override string GetSignatureForError ()
11077 return best_candidate.GetSignatureForError ();
11080 public override SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source)
11082 #if STATIC
11083 throw new NotSupportedException ();
11084 #else
11085 var value = new[] { source.MakeExpression (ctx) };
11086 var args = Arguments.MakeExpression (arguments, ctx).Concat (value);
11087 return SLE.Expression.Block (
11088 SLE.Expression.Call (InstanceExpression.MakeExpression (ctx), (MethodInfo) Setter.GetMetaInfo (), args),
11089 value [0]);
11090 #endif
11093 public override SLE.Expression MakeExpression (BuilderContext ctx)
11095 #if STATIC
11096 return base.MakeExpression (ctx);
11097 #else
11098 var args = Arguments.MakeExpression (arguments, ctx);
11099 return SLE.Expression.Call (InstanceExpression.MakeExpression (ctx), (MethodInfo) Getter.GetMetaInfo (), args);
11100 #endif
11103 protected override Expression OverloadResolve (ResolveContext rc, Expression right_side)
11105 if (best_candidate != null)
11106 return this;
11108 eclass = ExprClass.IndexerAccess;
11110 bool dynamic;
11111 using (rc.With (ResolveContext.Options.DontSetConditionalAccessReceiver, false)) {
11112 arguments.Resolve (rc, out dynamic);
11115 if (indexers == null && InstanceExpression.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
11116 dynamic = true;
11117 } else {
11118 var res = new OverloadResolver (indexers, OverloadResolver.Restrictions.None, loc);
11119 res.BaseMembersProvider = this;
11120 res.InstanceQualifier = this;
11122 // TODO: Do I need 2 argument sets?
11123 best_candidate = res.ResolveMember<IndexerSpec> (rc, ref arguments);
11124 if (best_candidate != null)
11125 type = res.BestCandidateReturnType;
11126 else if (!res.BestCandidateIsDynamic)
11127 return null;
11131 // It has dynamic arguments
11133 if (dynamic) {
11134 Arguments args = new Arguments (arguments.Count + 1);
11135 if (IsBase) {
11136 rc.Report.Error (1972, loc,
11137 "The indexer base access cannot be dynamically dispatched. Consider casting the dynamic arguments or eliminating the base access");
11138 } else {
11139 args.Add (new Argument (InstanceExpression));
11141 args.AddRange (arguments);
11143 best_candidate = null;
11144 return new DynamicIndexBinder (args, conditional_access_receiver, ConditionalAccess, loc);
11148 // Try to avoid resolving left expression again
11150 if (right_side != null)
11151 ResolveInstanceExpression (rc, right_side);
11153 return this;
11156 protected override void CloneTo (CloneContext clonectx, Expression t)
11158 IndexerExpr target = (IndexerExpr) t;
11160 if (arguments != null)
11161 target.arguments = arguments.Clone (clonectx);
11164 public void SetConditionalAccessReceiver ()
11166 conditional_access_receiver = true;
11169 public override void SetTypeArguments (ResolveContext ec, TypeArguments ta)
11171 Error_TypeArgumentsCannotBeUsed (ec, "indexer", GetSignatureForError (), loc);
11174 #region IBaseMembersProvider Members
11176 IList<MemberSpec> OverloadResolver.IBaseMembersProvider.GetBaseMembers (TypeSpec type)
11178 var baseType = type.BaseType;
11179 var members = baseType == null ? null : MemberCache.FindMembers (baseType, MemberCache.IndexerNameAlias, false);
11181 if (members == null && !type.IsInterface) {
11182 var tps = queried_type as TypeParameterSpec;
11183 if (tps != null)
11184 members = MemberCache.FindInterfaceMembers (tps, MemberCache.IndexerNameAlias);
11187 return members;
11190 IParametersMember OverloadResolver.IBaseMembersProvider.GetOverrideMemberParameters (MemberSpec member)
11192 if (queried_type == member.DeclaringType)
11193 return null;
11195 var filter = new MemberFilter (MemberCache.IndexerNameAlias, 0, MemberKind.Indexer, ((IndexerSpec) member).Parameters, null);
11196 return MemberCache.FindMember (queried_type, filter, BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as IParametersMember;
11199 MethodGroupExpr OverloadResolver.IBaseMembersProvider.LookupExtensionMethod (ResolveContext rc)
11201 return null;
11204 #endregion
11208 // A base access expression
11210 public class BaseThis : This
11212 public BaseThis (Location loc)
11213 : base (loc)
11217 public BaseThis (TypeSpec type, Location loc)
11218 : base (loc)
11220 this.type = type;
11221 eclass = ExprClass.Variable;
11224 #region Properties
11226 public override string Name {
11227 get {
11228 return "base";
11232 #endregion
11234 public override Expression CreateExpressionTree (ResolveContext ec)
11236 ec.Report.Error (831, loc, "An expression tree may not contain a base access");
11237 return base.CreateExpressionTree (ec);
11240 public override void Emit (EmitContext ec)
11242 base.Emit (ec);
11244 if (type == ec.Module.Compiler.BuiltinTypes.ValueType) {
11245 var context_type = ec.CurrentType;
11246 ec.Emit (OpCodes.Ldobj, context_type);
11247 ec.Emit (OpCodes.Box, context_type);
11251 protected override void Error_ThisNotAvailable (ResolveContext ec)
11253 if (ec.IsStatic) {
11254 ec.Report.Error (1511, loc, "Keyword `base' is not available in a static method");
11255 } else {
11256 ec.Report.Error (1512, loc, "Keyword `base' is not available in the current context");
11260 public override void ResolveBase (ResolveContext ec)
11262 base.ResolveBase (ec);
11263 type = ec.CurrentType.BaseType;
11266 public override object Accept (StructuralVisitor visitor)
11268 return visitor.Visit (this);
11272 /// <summary>
11273 /// This class exists solely to pass the Type around and to be a dummy
11274 /// that can be passed to the conversion functions (this is used by
11275 /// foreach implementation to typecast the object return value from
11276 /// get_Current into the proper type. All code has been generated and
11277 /// we only care about the side effect conversions to be performed
11279 /// This is also now used as a placeholder where a no-action expression
11280 /// is needed (the `New' class).
11281 /// </summary>
11282 public class EmptyExpression : Expression
11284 sealed class OutAccessExpression : EmptyExpression
11286 public OutAccessExpression (TypeSpec t)
11287 : base (t)
11291 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
11293 rc.Report.Error (206, right_side.Location,
11294 "A property, indexer or dynamic member access may not be passed as `ref' or `out' parameter");
11296 return null;
11300 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression (InternalType.FakeInternalType);
11301 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression (InternalType.FakeInternalType);
11302 public static readonly EmptyExpression UnaryAddress = new EmptyExpression (InternalType.FakeInternalType);
11303 public static readonly EmptyExpression EventAddition = new EmptyExpression (InternalType.FakeInternalType);
11304 public static readonly EmptyExpression EventSubtraction = new EmptyExpression (InternalType.FakeInternalType);
11305 public static readonly EmptyExpression MissingValue = new EmptyExpression (InternalType.FakeInternalType);
11306 public static readonly Expression Null = new EmptyExpression (InternalType.FakeInternalType);
11307 public static readonly EmptyExpression OutAccess = new OutAccessExpression (InternalType.FakeInternalType);
11309 public EmptyExpression (TypeSpec t)
11311 type = t;
11312 eclass = ExprClass.Value;
11313 loc = Location.Null;
11316 protected override void CloneTo (CloneContext clonectx, Expression target)
11320 public override bool ContainsEmitWithAwait ()
11322 return false;
11325 public override Expression CreateExpressionTree (ResolveContext ec)
11327 throw new NotSupportedException ("ET");
11330 protected override Expression DoResolve (ResolveContext ec)
11332 return this;
11335 public override void Emit (EmitContext ec)
11337 // nothing, as we only exist to not do anything.
11340 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
11344 public override void EmitSideEffect (EmitContext ec)
11348 public override object Accept (StructuralVisitor visitor)
11350 return visitor.Visit (this);
11354 sealed class EmptyAwaitExpression : EmptyExpression
11356 public EmptyAwaitExpression (TypeSpec type)
11357 : base (type)
11361 public override bool ContainsEmitWithAwait ()
11363 return true;
11368 // Empty statement expression
11370 public sealed class EmptyExpressionStatement : ExpressionStatement
11372 public static readonly EmptyExpressionStatement Instance = new EmptyExpressionStatement ();
11374 private EmptyExpressionStatement ()
11376 loc = Location.Null;
11379 public override bool ContainsEmitWithAwait ()
11381 return false;
11384 public override Expression CreateExpressionTree (ResolveContext ec)
11386 return null;
11389 public override void EmitStatement (EmitContext ec)
11391 // Do nothing
11394 protected override Expression DoResolve (ResolveContext ec)
11396 eclass = ExprClass.Value;
11397 type = ec.BuiltinTypes.Object;
11398 return this;
11401 public override void Emit (EmitContext ec)
11403 // Do nothing
11406 public override object Accept (StructuralVisitor visitor)
11408 return visitor.Visit (this);
11412 public class ErrorExpression : EmptyExpression
11414 public static readonly ErrorExpression Instance = new ErrorExpression ();
11416 private ErrorExpression ()
11417 : base (InternalType.ErrorType)
11421 public override Expression CreateExpressionTree (ResolveContext ec)
11423 return this;
11426 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
11428 return this;
11431 public override void Error_ValueAssignment (ResolveContext rc, Expression rhs)
11435 public override void Error_UnexpectedKind (ResolveContext ec, ResolveFlags flags, Location loc)
11439 public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl)
11443 public override void Error_OperatorCannotBeApplied (ResolveContext rc, Location loc, string oper, TypeSpec t)
11447 public override object Accept (StructuralVisitor visitor)
11449 return visitor.Visit (this);
11453 public class UserCast : Expression {
11454 MethodSpec method;
11455 Expression source;
11457 public UserCast (MethodSpec method, Expression source, Location l)
11459 if (source == null)
11460 throw new ArgumentNullException ("source");
11462 this.method = method;
11463 this.source = source;
11464 type = method.ReturnType;
11465 loc = l;
11468 public Expression Source {
11469 get {
11470 return source;
11472 set {
11473 source = value;
11477 public override bool ContainsEmitWithAwait ()
11479 return source.ContainsEmitWithAwait ();
11482 public override Expression CreateExpressionTree (ResolveContext ec)
11484 Arguments args = new Arguments (3);
11485 args.Add (new Argument (source.CreateExpressionTree (ec)));
11486 args.Add (new Argument (new TypeOf (type, loc)));
11487 args.Add (new Argument (new TypeOfMethod (method, loc)));
11488 return CreateExpressionFactoryCall (ec, "Convert", args);
11491 protected override Expression DoResolve (ResolveContext ec)
11493 method.CheckObsoleteness (ec, source.Location);
11495 eclass = ExprClass.Value;
11496 return this;
11499 public override void Emit (EmitContext ec)
11501 source.Emit (ec);
11502 ec.MarkCallEntry (loc);
11503 ec.Emit (OpCodes.Call, method);
11506 public override void FlowAnalysis (FlowAnalysisContext fc)
11508 source.FlowAnalysis (fc);
11511 public override string GetSignatureForError ()
11513 return TypeManager.CSharpSignature (method);
11516 public override SLE.Expression MakeExpression (BuilderContext ctx)
11518 #if STATIC
11519 return base.MakeExpression (ctx);
11520 #else
11521 return SLE.Expression.Convert (source.MakeExpression (ctx), type.GetMetaInfo (), (MethodInfo) method.GetMetaInfo ());
11522 #endif
11527 // Holds additional type specifiers like ?, *, []
11529 public class ComposedTypeSpecifier
11531 public static readonly ComposedTypeSpecifier SingleDimension = new ComposedTypeSpecifier (1, Location.Null);
11533 public readonly int Dimension;
11534 public readonly Location Location;
11536 public ComposedTypeSpecifier (int specifier, Location loc)
11538 this.Dimension = specifier;
11539 this.Location = loc;
11542 #region Properties
11543 public bool IsNullable {
11544 get {
11545 return Dimension == -1;
11549 public bool IsPointer {
11550 get {
11551 return Dimension == -2;
11555 public ComposedTypeSpecifier Next { get; set; }
11557 #endregion
11559 public static ComposedTypeSpecifier CreateArrayDimension (int dimension, Location loc)
11561 return new ComposedTypeSpecifier (dimension, loc);
11564 public static ComposedTypeSpecifier CreateNullable (Location loc)
11566 return new ComposedTypeSpecifier (-1, loc);
11569 public static ComposedTypeSpecifier CreatePointer (Location loc)
11571 return new ComposedTypeSpecifier (-2, loc);
11574 public string GetSignatureForError ()
11576 string s =
11577 IsPointer ? "*" :
11578 IsNullable ? "?" :
11579 ArrayContainer.GetPostfixSignature (Dimension);
11581 return Next != null ? s + Next.GetSignatureForError () : s;
11585 // <summary>
11586 // This class is used to "construct" the type during a typecast
11587 // operation. Since the Type.GetType class in .NET can parse
11588 // the type specification, we just use this to construct the type
11589 // one bit at a time.
11590 // </summary>
11591 public class ComposedCast : TypeExpr {
11592 FullNamedExpression left;
11593 ComposedTypeSpecifier spec;
11595 public ComposedCast (FullNamedExpression left, ComposedTypeSpecifier spec)
11597 if (spec == null)
11598 throw new ArgumentNullException ("spec");
11600 this.left = left;
11601 this.spec = spec;
11602 this.loc = left.Location;
11605 public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments)
11607 type = left.ResolveAsType (ec);
11608 if (type == null)
11609 return null;
11611 eclass = ExprClass.Type;
11613 var single_spec = spec;
11615 if (single_spec.IsNullable) {
11616 type = new Nullable.NullableType (type, loc).ResolveAsType (ec);
11617 if (type == null)
11618 return null;
11620 single_spec = single_spec.Next;
11621 } else if (single_spec.IsPointer) {
11623 // Declared fields cannot have unmanaged check done before all types are defined
11625 if (!(ec.CurrentMemberDefinition is Field) && !TypeManager.VerifyUnmanaged (ec.Module, type, loc))
11626 return null;
11628 var rc = ec as ResolveContext;
11629 if (rc?.CurrentIterator != null) {
11630 UnsafeInsideIteratorError (ec.Module.Compiler.Report, loc);
11631 } else if (!ec.IsUnsafe) {
11632 UnsafeError (ec.Module.Compiler.Report, loc);
11635 do {
11636 type = PointerContainer.MakeType (ec.Module, type);
11637 single_spec = single_spec.Next;
11638 } while (single_spec != null && single_spec.IsPointer);
11641 if (single_spec != null && single_spec.Dimension > 0) {
11642 if (type.IsSpecialRuntimeType || type.IsByRefLike) {
11643 ec.Module.Compiler.Report.Error (611, loc, "Array elements cannot be of type `{0}'", type.GetSignatureForError ());
11644 } else if (type.IsStatic) {
11645 ec.Module.Compiler.Report.SymbolRelatedToPreviousError (type);
11646 ec.Module.Compiler.Report.Error (719, loc, "Array elements cannot be of static type `{0}'",
11647 type.GetSignatureForError ());
11648 } else {
11649 MakeArray (ec.Module, single_spec);
11653 return type;
11656 void MakeArray (ModuleContainer module, ComposedTypeSpecifier spec)
11658 if (spec.Next != null)
11659 MakeArray (module, spec.Next);
11661 type = ArrayContainer.MakeType (module, type, spec.Dimension);
11664 public override string GetSignatureForError ()
11666 return left.GetSignatureForError () + spec.GetSignatureForError ();
11669 public override object Accept (StructuralVisitor visitor)
11671 return visitor.Visit (this);
11675 class ReferenceTypeExpr : TypeExpr
11677 FullNamedExpression element;
11678 readonly bool readOnly;
11680 public ReferenceTypeExpr (FullNamedExpression element, bool readOnly, Location loc)
11681 : this (element, loc)
11683 this.readOnly = readOnly;
11686 public ReferenceTypeExpr (FullNamedExpression element, Location loc)
11688 this.element = element;
11689 this.loc = loc;
11692 public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false)
11694 type = element.ResolveAsType (mc);
11695 if (type == null)
11696 return null;
11698 eclass = ExprClass.Type;
11699 type = readOnly ?
11700 ReadOnlyReferenceContainer.MakeType (mc.Module, type) :
11701 ReferenceContainer.MakeType (mc.Module, type);
11703 return type;
11706 public override string GetSignatureForError ()
11708 var prefix = readOnly ? "ref " : "ref readonly ";
11709 return prefix + element.GetSignatureForError ();
11712 public override object Accept (StructuralVisitor visitor)
11714 return visitor.Visit (this);
11718 class FixedBufferPtr : Expression
11720 readonly Expression array;
11722 public FixedBufferPtr (Expression array, TypeSpec array_type, Location l)
11724 this.type = array_type;
11725 this.array = array;
11726 this.loc = l;
11729 public override bool ContainsEmitWithAwait ()
11731 throw new NotImplementedException ();
11734 public override Expression CreateExpressionTree (ResolveContext ec)
11736 Error_PointerInsideExpressionTree (ec);
11737 return null;
11740 public override void Emit(EmitContext ec)
11742 array.Emit (ec);
11745 protected override Expression DoResolve (ResolveContext ec)
11747 type = PointerContainer.MakeType (ec.Module, type);
11748 eclass = ExprClass.Value;
11749 return this;
11755 // This class is used to represent the address of an array, used
11756 // only by the Fixed statement, this generates "&a [0]" construct
11757 // for fixed (char *pa = a)
11759 class ArrayPtr : FixedBufferPtr
11761 public ArrayPtr (Expression array, TypeSpec array_type, Location l):
11762 base (array, array_type, l)
11766 public override void Emit (EmitContext ec)
11768 base.Emit (ec);
11770 ec.EmitInt (0);
11771 ec.Emit (OpCodes.Ldelema, ((PointerContainer) type).Element);
11776 // Encapsulates a conversion rules required for array indexes
11778 public class ArrayIndexCast : TypeCast
11780 public ArrayIndexCast (Expression expr, TypeSpec returnType)
11781 : base (expr, returnType)
11783 if (expr.Type == returnType) // int -> int
11784 throw new ArgumentException ("unnecessary array index conversion");
11787 public override Expression CreateExpressionTree (ResolveContext ec)
11789 using (ec.Set (ResolveContext.Options.CheckedScope)) {
11790 return base.CreateExpressionTree (ec);
11794 public override void Emit (EmitContext ec)
11796 child.Emit (ec);
11798 switch (child.Type.BuiltinType) {
11799 case BuiltinTypeSpec.Type.UInt:
11800 ec.Emit (OpCodes.Conv_U);
11801 break;
11802 case BuiltinTypeSpec.Type.Long:
11803 ec.Emit (OpCodes.Conv_Ovf_I);
11804 break;
11805 case BuiltinTypeSpec.Type.ULong:
11806 ec.Emit (OpCodes.Conv_Ovf_I_Un);
11807 break;
11808 default:
11809 throw new InternalErrorException ("Cannot emit cast to unknown array element type", type);
11815 // Implements the `stackalloc' keyword
11817 public class StackAlloc : Expression {
11818 TypeSpec otype;
11819 Expression texpr;
11820 Expression count;
11822 public StackAlloc (Expression type, Expression count, Location l)
11824 texpr = type;
11825 this.count = count;
11826 loc = l;
11829 public Expression TypeExpression {
11830 get {
11831 return texpr;
11835 public Expression CountExpression {
11836 get {
11837 return this.count;
11841 public override bool ContainsEmitWithAwait ()
11843 return false;
11846 public override Expression CreateExpressionTree (ResolveContext ec)
11848 throw new NotSupportedException ("ET");
11851 protected override Expression DoResolve (ResolveContext ec)
11853 count = count.Resolve (ec);
11854 if (count == null)
11855 return null;
11857 if (count.Type.BuiltinType != BuiltinTypeSpec.Type.UInt){
11858 count = Convert.ImplicitConversionRequired (ec, count, ec.BuiltinTypes.Int, loc);
11859 if (count == null)
11860 return null;
11863 Constant c = count as Constant;
11864 if (c != null && c.IsNegative) {
11865 ec.Report.Error (247, loc, "Cannot use a negative size with stackalloc");
11868 if (ec.HasAny (ResolveContext.Options.CatchScope | ResolveContext.Options.FinallyScope)) {
11869 ec.Report.Error (255, loc, "Cannot use stackalloc in finally or catch");
11872 otype = texpr.ResolveAsType (ec);
11873 if (otype == null)
11874 return null;
11876 if (!TypeManager.VerifyUnmanaged (ec.Module, otype, loc))
11877 return null;
11879 type = PointerContainer.MakeType (ec.Module, otype);
11880 eclass = ExprClass.Value;
11882 return this;
11885 public override void Emit (EmitContext ec)
11887 int size = BuiltinTypeSpec.GetSize (otype);
11889 count.Emit (ec);
11891 if (size == 0)
11892 ec.Emit (OpCodes.Sizeof, otype);
11893 else
11894 ec.EmitInt (size);
11896 ec.Emit (OpCodes.Mul_Ovf_Un);
11897 ec.Emit (OpCodes.Localloc);
11900 protected override void CloneTo (CloneContext clonectx, Expression t)
11902 StackAlloc target = (StackAlloc) t;
11903 target.count = count.Clone (clonectx);
11904 target.texpr = texpr.Clone (clonectx);
11907 public override object Accept (StructuralVisitor visitor)
11909 return visitor.Visit (this);
11914 // An object initializer expression
11916 public class ElementInitializer : Assign
11918 public readonly string Name;
11920 public ElementInitializer (string name, Expression initializer, Location loc)
11921 : base (null, initializer, loc)
11923 this.Name = name;
11926 public bool IsDictionaryInitializer {
11927 get {
11928 return Name == null;
11932 protected override void CloneTo (CloneContext clonectx, Expression t)
11934 ElementInitializer target = (ElementInitializer) t;
11935 target.source = source.Clone (clonectx);
11938 public override Expression CreateExpressionTree (ResolveContext ec)
11940 Arguments args = new Arguments (2);
11941 FieldExpr fe = target as FieldExpr;
11942 if (fe != null)
11943 args.Add (new Argument (fe.CreateTypeOfExpression ()));
11944 else
11945 args.Add (new Argument (((PropertyExpr) target).CreateSetterTypeOfExpression (ec)));
11947 string mname;
11948 Expression arg_expr;
11949 var cinit = source as CollectionOrObjectInitializers;
11950 if (cinit == null) {
11951 mname = "Bind";
11952 arg_expr = source.CreateExpressionTree (ec);
11953 } else {
11954 mname = cinit.IsEmpty || cinit.Initializers[0] is ElementInitializer ? "MemberBind" : "ListBind";
11955 arg_expr = cinit.CreateExpressionTree (ec, !cinit.IsEmpty);
11958 args.Add (new Argument (arg_expr));
11959 return CreateExpressionFactoryCall (ec, mname, args);
11962 protected override Expression DoResolve (ResolveContext ec)
11964 if (source == null)
11965 return EmptyExpressionStatement.Instance;
11967 if (!ResolveElement (ec))
11968 return null;
11970 if (source is CollectionOrObjectInitializers) {
11971 target = target.Resolve (ec);
11972 if (target == null)
11973 return null;
11975 Expression previous = ec.CurrentInitializerVariable;
11976 ec.CurrentInitializerVariable = target;
11977 source = source.Resolve (ec);
11978 ec.CurrentInitializerVariable = previous;
11979 if (source == null)
11980 return null;
11982 eclass = source.eclass;
11983 type = source.Type;
11985 return this;
11988 return base.DoResolve (ec);
11991 public override void EmitStatement (EmitContext ec)
11993 if (source is CollectionOrObjectInitializers)
11994 source.Emit (ec);
11995 else
11996 base.EmitStatement (ec);
11999 protected virtual bool ResolveElement (ResolveContext rc)
12001 var t = rc.CurrentInitializerVariable.Type;
12002 if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
12003 Arguments args = new Arguments (1);
12004 args.Add (new Argument (rc.CurrentInitializerVariable));
12005 target = new DynamicMemberBinder (Name, args, loc);
12006 } else {
12007 var member = MemberLookup (rc, false, t, Name, 0, MemberLookupRestrictions.ExactArity, loc);
12008 if (member == null) {
12009 member = Expression.MemberLookup (rc, true, t, Name, 0, MemberLookupRestrictions.ExactArity, loc);
12011 if (member != null) {
12012 // TODO: ec.Report.SymbolRelatedToPreviousError (member);
12013 ErrorIsInaccesible (rc, member.GetSignatureForError (), loc);
12014 return false;
12018 if (member == null) {
12019 Error_TypeDoesNotContainDefinition (rc, loc, t, Name);
12020 return false;
12023 var me = member as MemberExpr;
12024 if (me is EventExpr) {
12025 me = me.ResolveMemberAccess (rc, null, null);
12026 } else if (!(member is PropertyExpr || member is FieldExpr)) {
12027 rc.Report.Error (1913, loc,
12028 "Member `{0}' cannot be initialized. An object initializer may only be used for fields, or properties",
12029 member.GetSignatureForError ());
12031 return false;
12034 if (me.IsStatic) {
12035 rc.Report.Error (1914, loc,
12036 "Static field or property `{0}' cannot be assigned in an object initializer",
12037 me.GetSignatureForError ());
12040 target = me;
12041 me.InstanceExpression = rc.CurrentInitializerVariable;
12044 return true;
12049 // A collection initializer expression
12051 class CollectionElementInitializer : Invocation
12053 public class ElementInitializerArgument : Argument
12055 public ElementInitializerArgument (Expression e)
12056 : base (e)
12061 sealed class AddMemberAccess : MemberAccess
12063 public AddMemberAccess (Expression expr, Location loc)
12064 : base (expr, "Add", loc)
12068 public override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name)
12070 if (TypeManager.HasElementType (type))
12071 return;
12073 base.Error_TypeDoesNotContainDefinition (ec, type, name);
12077 public CollectionElementInitializer (Expression argument)
12078 : base (null, new Arguments (1))
12080 base.arguments.Add (new ElementInitializerArgument (argument));
12081 this.loc = argument.Location;
12084 public CollectionElementInitializer (List<Expression> arguments, Location loc)
12085 : base (null, new Arguments (arguments.Count))
12087 foreach (Expression e in arguments)
12088 base.arguments.Add (new ElementInitializerArgument (e));
12090 this.loc = loc;
12093 public CollectionElementInitializer (Location loc)
12094 : base (null, null)
12096 this.loc = loc;
12099 public override Expression CreateExpressionTree (ResolveContext ec)
12101 Arguments args = new Arguments (2);
12102 args.Add (new Argument (mg.CreateExpressionTree (ec)));
12104 var expr_initializers = new ArrayInitializer (arguments.Count, loc);
12105 foreach (Argument a in arguments) {
12106 if (a.ArgType == Argument.AType.ExtensionType) {
12107 ec.Report.Error (8075, a.Expr.Location, "An expression tree cannot contain a collection initializer with extension method");
12108 continue;
12110 expr_initializers.Add (a.CreateExpressionTree (ec));
12113 args.Add (new Argument (new ArrayCreation (
12114 CreateExpressionTypeExpression (ec, loc), expr_initializers, loc)));
12115 return CreateExpressionFactoryCall (ec, "ElementInit", args);
12118 protected override void CloneTo (CloneContext clonectx, Expression t)
12120 CollectionElementInitializer target = (CollectionElementInitializer) t;
12121 if (arguments != null)
12122 target.arguments = arguments.Clone (clonectx);
12125 protected override Expression DoResolve (ResolveContext ec)
12127 base.expr = new AddMemberAccess (ec.CurrentInitializerVariable, loc);
12129 return base.DoResolve (ec);
12133 class DictionaryElementInitializer : ElementInitializer
12135 readonly Arguments args;
12137 public DictionaryElementInitializer (Arguments arguments, Expression initializer, Location loc)
12138 : base (null, initializer, loc)
12140 this.args = arguments;
12143 public override Expression CreateExpressionTree (ResolveContext ec)
12145 ec.Report.Error (8074, loc, "Expression tree cannot contain a dictionary initializer");
12146 return null;
12149 protected override bool ResolveElement (ResolveContext rc)
12151 var init = rc.CurrentInitializerVariable;
12152 var type = init.Type;
12154 if (type.IsArray) {
12155 target = new ArrayAccess (new ElementAccess (init, args, loc), loc);
12156 return true;
12159 if (type.IsPointer) {
12160 target = init.MakePointerAccess (rc, type, args);
12161 return true;
12164 var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false);
12165 if (indexers == null && type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) {
12166 ElementAccess.Error_CannotApplyIndexing (rc, type, loc);
12167 return false;
12170 target = new IndexerExpr (indexers, type, init, args, loc);
12171 return true;
12176 // A block of object or collection initializers
12178 public class CollectionOrObjectInitializers : ExpressionStatement
12180 IList<Expression> initializers;
12181 bool is_collection_initialization;
12183 public CollectionOrObjectInitializers (Location loc)
12184 : this (new Expression[0], loc)
12188 public CollectionOrObjectInitializers (IList<Expression> initializers, Location loc)
12190 this.initializers = initializers;
12191 this.loc = loc;
12194 public IList<Expression> Initializers {
12195 get {
12196 return initializers;
12200 public bool IsEmpty {
12201 get {
12202 return initializers.Count == 0;
12206 public bool IsCollectionInitializer {
12207 get {
12208 return is_collection_initialization;
12212 protected override void CloneTo (CloneContext clonectx, Expression target)
12214 CollectionOrObjectInitializers t = (CollectionOrObjectInitializers) target;
12216 t.initializers = new List<Expression> (initializers.Count);
12217 foreach (var e in initializers)
12218 t.initializers.Add (e.Clone (clonectx));
12221 public override bool ContainsEmitWithAwait ()
12223 foreach (var e in initializers) {
12224 if (e.ContainsEmitWithAwait ())
12225 return true;
12228 return false;
12231 public override Expression CreateExpressionTree (ResolveContext ec)
12233 return CreateExpressionTree (ec, false);
12236 public Expression CreateExpressionTree (ResolveContext ec, bool inferType)
12238 var expr_initializers = new ArrayInitializer (initializers.Count, loc);
12239 foreach (Expression e in initializers) {
12240 Expression expr = e.CreateExpressionTree (ec);
12241 if (expr != null)
12242 expr_initializers.Add (expr);
12245 if (inferType)
12246 return new ImplicitlyTypedArrayCreation (expr_initializers, loc);
12248 return new ArrayCreation (new TypeExpression (ec.Module.PredefinedTypes.MemberBinding.Resolve (), loc), expr_initializers, loc);
12251 protected override Expression DoResolve (ResolveContext ec)
12253 List<string> element_names = null;
12254 for (int i = 0; i < initializers.Count; ++i) {
12255 Expression initializer = initializers [i];
12256 ElementInitializer element_initializer = initializer as ElementInitializer;
12258 if (i == 0) {
12259 if (element_initializer != null) {
12260 element_names = new List<string> (initializers.Count);
12261 if (!element_initializer.IsDictionaryInitializer)
12262 element_names.Add (element_initializer.Name);
12263 } else if (initializer is CompletingExpression) {
12264 initializer.Resolve (ec);
12265 throw new InternalErrorException ("This line should never be reached");
12266 } else {
12267 var t = ec.CurrentInitializerVariable.Type;
12268 // LAMESPEC: The collection must implement IEnumerable only, no dynamic support
12269 if (!t.ImplementsInterface (ec.BuiltinTypes.IEnumerable, false) && t.BuiltinType != BuiltinTypeSpec.Type.Dynamic) {
12270 ec.Report.Error (1922, loc, "A field or property `{0}' cannot be initialized with a collection " +
12271 "object initializer because type `{1}' does not implement `{2}' interface",
12272 ec.CurrentInitializerVariable.GetSignatureForError (),
12273 ec.CurrentInitializerVariable.Type.GetSignatureForError (),
12274 ec.BuiltinTypes.IEnumerable.GetSignatureForError ());
12275 return null;
12277 is_collection_initialization = true;
12279 } else {
12280 if (is_collection_initialization != (element_initializer == null)) {
12281 ec.Report.Error (747, initializer.Location, "Inconsistent `{0}' member declaration",
12282 is_collection_initialization ? "collection initializer" : "object initializer");
12283 continue;
12286 if (!is_collection_initialization && !element_initializer.IsDictionaryInitializer) {
12287 if (element_names.Contains (element_initializer.Name)) {
12288 ec.Report.Error (1912, element_initializer.Location,
12289 "An object initializer includes more than one member `{0}' initialization",
12290 element_initializer.Name);
12291 } else {
12292 element_names.Add (element_initializer.Name);
12297 Expression e = initializer.Resolve (ec);
12298 if (e == EmptyExpressionStatement.Instance)
12299 initializers.RemoveAt (i--);
12300 else
12301 initializers [i] = e;
12304 type = ec.CurrentInitializerVariable.Type;
12305 if (is_collection_initialization) {
12306 if (TypeManager.HasElementType (type)) {
12307 ec.Report.Error (1925, loc, "Cannot initialize object of type `{0}' with a collection initializer",
12308 type.GetSignatureForError ());
12312 eclass = ExprClass.Variable;
12313 return this;
12316 public override void Emit (EmitContext ec)
12318 EmitStatement (ec);
12321 public override void EmitStatement (EmitContext ec)
12323 foreach (ExpressionStatement e in initializers) {
12324 // TODO: need location region
12325 ec.Mark (e.Location);
12326 e.EmitStatement (ec);
12330 public override void FlowAnalysis (FlowAnalysisContext fc)
12332 foreach (var initializer in initializers) {
12333 if (initializer != null)
12334 initializer.FlowAnalysis (fc);
12340 // New expression with element/object initializers
12342 public class NewInitialize : New
12345 // This class serves as a proxy for variable initializer target instances.
12346 // A real variable is assigned later when we resolve left side of an
12347 // assignment
12349 sealed class InitializerTargetExpression : Expression, IMemoryLocation
12351 NewInitialize new_instance;
12353 public InitializerTargetExpression (NewInitialize newInstance)
12355 this.type = newInstance.type;
12356 this.loc = newInstance.loc;
12357 this.eclass = newInstance.eclass;
12358 this.new_instance = newInstance;
12361 public override bool ContainsEmitWithAwait ()
12363 return false;
12366 public override Expression CreateExpressionTree (ResolveContext ec)
12368 // Should not be reached
12369 throw new NotSupportedException ("ET");
12372 protected override Expression DoResolve (ResolveContext ec)
12374 return this;
12377 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
12379 return this;
12382 public override void Emit (EmitContext ec)
12384 Expression e = (Expression) new_instance.instance;
12385 e.Emit (ec);
12388 public override Expression EmitToField (EmitContext ec)
12390 return (Expression) new_instance.instance;
12393 #region IMemoryLocation Members
12395 public void AddressOf (EmitContext ec, AddressOp mode)
12397 new_instance.instance.AddressOf (ec, mode);
12400 #endregion
12403 CollectionOrObjectInitializers initializers;
12404 IMemoryLocation instance;
12405 DynamicExpressionStatement dynamic;
12407 public NewInitialize (FullNamedExpression requested_type, Arguments arguments, CollectionOrObjectInitializers initializers, Location l)
12408 : base (requested_type, arguments, l)
12410 this.initializers = initializers;
12413 public CollectionOrObjectInitializers Initializers {
12414 get {
12415 return initializers;
12419 protected override void CloneTo (CloneContext clonectx, Expression t)
12421 base.CloneTo (clonectx, t);
12423 NewInitialize target = (NewInitialize) t;
12424 target.initializers = (CollectionOrObjectInitializers) initializers.Clone (clonectx);
12427 public override bool ContainsEmitWithAwait ()
12429 return base.ContainsEmitWithAwait () || initializers.ContainsEmitWithAwait ();
12432 public override Expression CreateExpressionTree (ResolveContext ec)
12434 Arguments args = new Arguments (2);
12435 args.Add (new Argument (base.CreateExpressionTree (ec)));
12436 if (!initializers.IsEmpty)
12437 args.Add (new Argument (initializers.CreateExpressionTree (ec, initializers.IsCollectionInitializer)));
12439 return CreateExpressionFactoryCall (ec,
12440 initializers.IsCollectionInitializer ? "ListInit" : "MemberInit",
12441 args);
12444 protected override Expression DoResolve (ResolveContext rc)
12446 Expression e = base.DoResolve (rc);
12447 if (type == null)
12448 return null;
12450 if (type.IsDelegate) {
12451 rc.Report.Error (1958, Initializers.Location,
12452 "Object and collection initializers cannot be used to instantiate a delegate");
12455 Expression previous = rc.CurrentInitializerVariable;
12456 rc.CurrentInitializerVariable = new InitializerTargetExpression (this);
12457 using (rc.With (ResolveContext.Options.DontSetConditionalAccessReceiver, false)) {
12458 initializers.Resolve (rc);
12460 rc.CurrentInitializerVariable = previous;
12462 dynamic = e as DynamicExpressionStatement;
12463 if (dynamic != null)
12464 return this;
12466 return e;
12469 public override void Emit (EmitContext ec)
12471 if (!CanEmitOptimizedLocalTarget (ec)) {
12472 var fe = ec.GetTemporaryField (type);
12474 if (!Emit (ec, fe))
12475 fe.Emit (ec);
12477 return;
12480 base.Emit (ec);
12483 public override bool Emit (EmitContext ec, IMemoryLocation target)
12486 // Expression is initialized into temporary target then moved
12487 // to real one for atomicity
12489 IMemoryLocation temp_target = target;
12491 LocalTemporary temp = null;
12492 bool by_ref = false;
12493 if (!initializers.IsEmpty) {
12494 temp_target = target as LocalTemporary;
12495 if (temp_target == null)
12496 temp_target = target as StackFieldExpr;
12498 if (temp_target == null) {
12499 var vr = target as VariableReference;
12500 if (vr != null && vr.IsRef) {
12501 vr.EmitLoad (ec);
12502 by_ref = true;
12506 if (temp_target == null)
12507 temp_target = temp = new LocalTemporary (type);
12510 bool left_on_stack;
12511 if (dynamic != null) {
12512 dynamic.Emit (ec);
12513 left_on_stack = true;
12514 } else {
12515 left_on_stack = base.Emit (ec, temp_target);
12518 if (initializers.IsEmpty)
12519 return left_on_stack;
12521 StackFieldExpr sf = null;
12523 // Move a new instance (reference-type) to local temporary variable
12524 if (left_on_stack) {
12525 if (by_ref) {
12526 temp_target = temp = new LocalTemporary (type);
12529 if (temp != null)
12530 temp.Store (ec);
12532 if (ec.HasSet (BuilderContext.Options.AsyncBody) && initializers.ContainsEmitWithAwait ()) {
12533 if (temp == null)
12534 throw new NotImplementedException ();
12536 sf = ec.GetTemporaryField (type);
12537 sf.AutomaticallyReuse = false;
12538 sf.EmitAssign (ec, temp, false, false);
12539 temp_target = sf;
12540 temp.Release (ec);
12541 left_on_stack = false;
12545 instance = temp_target;
12547 initializers.Emit (ec);
12549 ((Expression)temp_target).Emit (ec);
12551 if (temp != null)
12552 temp.Release (ec);
12554 if (sf != null)
12555 sf.PrepareCleanup (ec);
12557 return true;
12560 public override bool CanEmitOptimizedLocalTarget (EmitContext ec)
12562 return !(method == null && TypeSpec.IsValueType (type) &&
12563 initializers.Initializers.Count > 1 && ec.HasSet (BuilderContext.Options.AsyncBody) &&
12564 initializers.ContainsEmitWithAwait ());
12567 protected override IMemoryLocation EmitAddressOf (EmitContext ec, AddressOp Mode)
12569 instance = base.EmitAddressOf (ec, Mode);
12571 if (!initializers.IsEmpty)
12572 initializers.Emit (ec);
12574 return instance;
12577 public override void FlowAnalysis (FlowAnalysisContext fc)
12579 base.FlowAnalysis (fc);
12580 initializers.FlowAnalysis (fc);
12583 public override object Accept (StructuralVisitor visitor)
12585 return visitor.Visit (this);
12589 public class NewAnonymousType : New
12591 static readonly AnonymousTypeParameter[] EmptyParameters = new AnonymousTypeParameter[0];
12593 List<AnonymousTypeParameter> parameters;
12594 readonly TypeContainer parent;
12595 AnonymousTypeClass anonymous_type;
12597 public NewAnonymousType (List<AnonymousTypeParameter> parameters, TypeContainer parent, Location loc)
12598 : base (null, null, loc)
12600 this.parameters = parameters;
12601 this.parent = parent;
12604 public List<AnonymousTypeParameter> Parameters {
12605 get {
12606 return this.parameters;
12610 protected override void CloneTo (CloneContext clonectx, Expression target)
12612 if (parameters == null)
12613 return;
12615 NewAnonymousType t = (NewAnonymousType) target;
12616 t.parameters = new List<AnonymousTypeParameter> (parameters.Count);
12617 foreach (AnonymousTypeParameter atp in parameters)
12618 t.parameters.Add ((AnonymousTypeParameter) atp.Clone (clonectx));
12621 AnonymousTypeClass CreateAnonymousType (ResolveContext ec, IList<AnonymousTypeParameter> parameters)
12623 AnonymousTypeClass type = parent.Module.GetAnonymousType (parameters);
12624 if (type != null)
12625 return type;
12627 type = AnonymousTypeClass.Create (parent, parameters, loc);
12628 if (type == null)
12629 return null;
12631 int errors = ec.Report.Errors;
12632 type.CreateContainer ();
12633 type.DefineContainer ();
12634 type.ExpandBaseInterfaces ();
12635 type.Define ();
12636 if ((ec.Report.Errors - errors) == 0) {
12637 parent.Module.AddAnonymousType (type);
12638 type.PrepareEmit ();
12641 return type;
12644 public override Expression CreateExpressionTree (ResolveContext ec)
12646 if (parameters == null)
12647 return base.CreateExpressionTree (ec);
12649 var init = new ArrayInitializer (parameters.Count, loc);
12650 foreach (var m in anonymous_type.Members) {
12651 var p = m as Property;
12652 if (p != null)
12653 init.Add (new TypeOfMethod (MemberCache.GetMember (type, p.Get.Spec), loc));
12656 var ctor_args = new ArrayInitializer (arguments.Count, loc);
12657 foreach (Argument a in arguments)
12658 ctor_args.Add (a.CreateExpressionTree (ec));
12660 Arguments args = new Arguments (3);
12661 args.Add (new Argument (new TypeOfMethod (method, loc)));
12662 args.Add (new Argument (new ArrayCreation (CreateExpressionTypeExpression (ec, loc), ctor_args, loc)));
12663 args.Add (new Argument (new ImplicitlyTypedArrayCreation (init, loc)));
12665 return CreateExpressionFactoryCall (ec, "New", args);
12668 protected override Expression DoResolve (ResolveContext ec)
12670 if (ec.HasSet (ResolveContext.Options.ConstantScope)) {
12671 ec.Report.Error (836, loc, "Anonymous types cannot be used in this expression");
12672 return null;
12675 if (parameters == null) {
12676 anonymous_type = CreateAnonymousType (ec, EmptyParameters);
12677 RequestedType = new TypeExpression (anonymous_type.Definition, loc);
12678 return base.DoResolve (ec);
12681 bool error = false;
12682 arguments = new Arguments (parameters.Count);
12683 var t_args = new TypeSpec [parameters.Count];
12684 for (int i = 0; i < parameters.Count; ++i) {
12685 Expression e = parameters [i].Resolve (ec);
12686 if (e == null) {
12687 error = true;
12688 continue;
12691 arguments.Add (new Argument (e));
12692 t_args [i] = e.Type;
12695 if (error)
12696 return null;
12698 anonymous_type = CreateAnonymousType (ec, parameters);
12699 if (anonymous_type == null)
12700 return null;
12702 type = anonymous_type.Definition.MakeGenericType (ec.Module, t_args);
12703 method = (MethodSpec) MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly);
12704 eclass = ExprClass.Value;
12705 return this;
12708 public override object Accept (StructuralVisitor visitor)
12710 return visitor.Visit (this);
12714 public class AnonymousTypeParameter : ShimExpression
12716 public readonly string Name;
12718 public AnonymousTypeParameter (Expression initializer, string name, Location loc)
12719 : base (initializer)
12721 this.Name = name;
12722 this.loc = loc;
12725 public AnonymousTypeParameter (Parameter parameter)
12726 : base (new SimpleName (parameter.Name, parameter.Location))
12728 this.Name = parameter.Name;
12729 this.loc = parameter.Location;
12732 public override bool Equals (object o)
12734 AnonymousTypeParameter other = o as AnonymousTypeParameter;
12735 return other != null && Name == other.Name;
12738 public override int GetHashCode ()
12740 return Name.GetHashCode ();
12743 protected override Expression DoResolve (ResolveContext ec)
12745 Expression e = expr.Resolve (ec);
12746 if (e == null)
12747 return null;
12749 if (e.eclass == ExprClass.MethodGroup) {
12750 Error_InvalidInitializer (ec, e.ExprClassName);
12751 return null;
12754 type = e.Type;
12755 if (type.Kind == MemberKind.Void || InternalType.HasNoType (type) || type.IsPointer || (e is TupleLiteral && TupleLiteral.ContainsNoTypeElement (type))) {
12756 Error_InvalidInitializer (ec, type.GetSignatureForError ());
12757 return null;
12760 return e;
12763 protected virtual void Error_InvalidInitializer (ResolveContext ec, string initializer)
12765 ec.Report.Error (828, loc, "An anonymous type property `{0}' cannot be initialized with `{1}'",
12766 Name, initializer);
12770 public class CatchFilterExpression : BooleanExpression
12772 public CatchFilterExpression (Expression expr, Location loc)
12773 : base (expr)
12775 this.loc = loc;
12779 public class InterpolatedString : Expression
12781 readonly StringLiteral start, end;
12782 List<Expression> interpolations;
12783 Arguments arguments;
12785 public InterpolatedString (StringLiteral start, List<Expression> interpolations, StringLiteral end)
12787 this.start = start;
12788 this.end = end;
12789 this.interpolations = interpolations;
12790 loc = start.Location;
12793 protected override void CloneTo (CloneContext clonectx, Expression t)
12795 InterpolatedString target = (InterpolatedString) t;
12797 if (interpolations != null) {
12798 target.interpolations = new List<Expression> ();
12799 foreach (var interpolation in interpolations) {
12800 target.interpolations.Add (interpolation.Clone (clonectx));
12805 public Expression ConvertTo (ResolveContext rc, TypeSpec type)
12807 var factory = rc.Module.PredefinedTypes.FormattableStringFactory.Resolve ();
12808 if (factory == null)
12809 return null;
12811 var ma = new MemberAccess (new TypeExpression (factory, loc), "Create", loc);
12812 var res = new Invocation (ma, arguments).Resolve (rc);
12813 if (res != null && res.Type != type)
12814 res = Convert.ExplicitConversion (rc, res, type, loc);
12816 return res;
12819 public override bool ContainsEmitWithAwait ()
12821 if (interpolations == null)
12822 return false;
12824 foreach (var expr in interpolations) {
12825 if (expr.ContainsEmitWithAwait ())
12826 return true;
12829 return false;
12832 public override Expression CreateExpressionTree (ResolveContext rc)
12834 var best = ResolveBestFormatOverload (rc);
12835 if (best == null)
12836 return null;
12838 Expression instance = new NullLiteral (loc);
12839 var args = Arguments.CreateForExpressionTree (rc, arguments, instance, new TypeOfMethod (best, loc));
12840 return CreateExpressionFactoryCall (rc, "Call", args);
12843 protected override Expression DoResolve (ResolveContext rc)
12845 string str;
12847 if (interpolations == null) {
12848 str = start.Value;
12849 arguments = new Arguments (1);
12850 } else {
12851 arguments = new Arguments (interpolations.Count);
12853 var sb = new StringBuilder (start.Value);
12854 for (int i = 0; i < interpolations.Count; ++i) {
12855 if (i % 2 == 0) {
12856 sb.Append ('{').Append (i / 2);
12857 var isi = (InterpolatedStringInsert)interpolations [i];
12858 if (isi.Alignment != null) {
12859 sb.Append (',');
12860 var value = isi.ResolveAligment (rc);
12861 if (value != null)
12862 sb.Append (value.Value);
12865 if (isi.Format != null) {
12866 sb.Append (':');
12867 sb.Append (isi.Format);
12870 sb.Append ('}');
12871 arguments.Add (new Argument (isi.Resolve (rc)));
12872 } else {
12873 sb.Append (((StringLiteral)interpolations [i]).Value);
12877 sb.Append (end.Value);
12878 str = sb.ToString ();
12881 arguments.Insert (0, new Argument (new StringLiteral (rc.BuiltinTypes, str, start.Location)));
12883 eclass = ExprClass.Value;
12884 type = rc.BuiltinTypes.String;
12885 return this;
12888 public override void Emit (EmitContext ec)
12890 // No interpolation, convert to simple string result (needs to match string.Format unescaping)
12891 if (interpolations == null) {
12892 var str = start.Value.Replace ("{{", "{").Replace ("}}", "}");
12893 if (str != start.Value)
12894 new StringConstant (ec.BuiltinTypes, str, loc).Emit (ec);
12895 else
12896 start.Emit (ec);
12898 return;
12901 var best = ResolveBestFormatOverload (new ResolveContext (ec.MemberContext));
12902 if (best == null)
12903 return;
12905 var ca = new CallEmitter ();
12906 ca.Emit (ec, best, arguments, loc);
12909 public override void FlowAnalysis (FlowAnalysisContext fc)
12911 if (interpolations != null) {
12912 foreach (var expr in interpolations) {
12913 expr.FlowAnalysis (fc);
12918 MethodSpec ResolveBestFormatOverload (ResolveContext rc)
12920 var members = MemberCache.FindMembers (rc.BuiltinTypes.String, "Format", true);
12921 var res = new OverloadResolver (members, OverloadResolver.Restrictions.NoBaseMembers, loc);
12922 return res.ResolveMember<MethodSpec> (rc, ref arguments);
12926 public class InterpolatedStringInsert : CompositeExpression
12928 public InterpolatedStringInsert (Expression expr)
12929 : base (expr)
12933 public Expression Alignment { get; set; }
12934 public string Format { get; set; }
12936 protected override void CloneTo (CloneContext clonectx, Expression t)
12938 var target = (InterpolatedStringInsert)t;
12939 target.expr = expr.Clone (clonectx);
12940 if (Alignment != null)
12941 target.Alignment = Alignment.Clone (clonectx);
12944 protected override Expression DoResolve (ResolveContext rc)
12946 var expr = base.DoResolve (rc);
12947 if (expr == null)
12948 return null;
12951 // For better error reporting, assumes the built-in implementation uses object
12952 // as argument(s)
12954 return Convert.ImplicitConversionRequired (rc, expr, rc.BuiltinTypes.Object, expr.Location);
12957 public override void FlowAnalysis (FlowAnalysisContext fc)
12959 Child.FlowAnalysis (fc);
12962 public int? ResolveAligment (ResolveContext rc)
12964 var c = Alignment.ResolveLabelConstant (rc);
12965 if (c == null)
12966 return null;
12968 c = c.ImplicitConversionRequired (rc, rc.BuiltinTypes.Int);
12969 if (c == null)
12970 return null;
12972 var value = (int) c.GetValueAsLong ();
12973 if (value > 32767 || value < -32767) {
12974 rc.Report.Warning (8094, 1, Alignment.Location,
12975 "Alignment value has a magnitude greater than 32767 and may result in a large formatted string");
12978 return value;
12982 class ThrowExpression : ExpressionStatement
12984 Expression expr;
12986 public ThrowExpression (Expression expr, Location loc)
12988 this.expr = expr;
12989 this.loc = loc;
12992 protected override void CloneTo (CloneContext clonectx, Expression t)
12994 var target = (ThrowExpression)t;
12995 target.expr = expr.Clone (clonectx);
12998 public override bool ContainsEmitWithAwait ()
13000 return expr.ContainsEmitWithAwait ();
13003 public override Expression CreateExpressionTree (ResolveContext rc)
13005 rc.Report.Error (8188, loc, "An expression tree cannot not contain a throw expression");
13006 return expr;
13009 protected override Expression DoResolve (ResolveContext rc)
13011 expr = expr.Resolve (rc, ResolveFlags.Type | ResolveFlags.VariableOrValue);
13013 if (expr == null)
13014 return null;
13016 expr = Throw.ConvertType (rc, expr);
13018 eclass = ExprClass.Value;
13019 type = InternalType.ThrowExpr;
13020 return this;
13023 public override void Emit (EmitContext ec)
13025 EmitStatement (ec);
13028 public override void EmitStatement (EmitContext ec)
13030 expr.Emit (ec);
13032 ec.Emit (OpCodes.Throw);
13035 public override void FlowAnalysis (FlowAnalysisContext fc)
13037 expr.FlowAnalysis (fc);
13040 public override Reachability MarkReachable (Reachability rc)
13042 return Reachability.CreateUnreachable ();
13046 class ReferenceExpression : CompositeExpression
13048 public ReferenceExpression (Expression expr, Location loc)
13049 : base (expr)
13051 this.loc = loc;
13054 static bool CanBeByRef (Expression expr)
13056 if (expr is IAssignMethod)
13057 return true;
13059 var invocation = expr as Invocation;
13060 if (invocation?.Type.Kind == MemberKind.ByRef)
13061 return true;
13063 return false;
13066 public override Expression CreateExpressionTree (ResolveContext rc)
13068 throw new NotSupportedException ("ET");
13071 protected override Expression DoResolve (ResolveContext rc)
13073 var res = expr.DoResolveLValue (rc, EmptyExpression.OutAccess);
13074 if (res == null || !CanBeByRef (res)) {
13075 if (res?.Type != InternalType.ErrorType)
13076 rc.Report.Error (8156, expr.Location, "An expression cannot be used in this context because it may not be returned by reference");
13077 return ErrorExpression.Instance;
13080 type = res.Type;
13081 var type_container = type as ReferenceContainer;
13082 if (type_container != null)
13083 type = type_container.Element;
13085 expr = res;
13086 eclass = ExprClass.Value;
13087 return this;
13090 public override void Emit (EmitContext ec)
13092 var ml = expr as IMemoryLocation;
13093 if (ml != null)
13094 ml.AddressOf (ec, AddressOp.LoadStore);
13095 else
13096 expr.Emit (ec);
13099 public override void Error_ValueCannotBeConverted (ResolveContext rc, TypeSpec target, bool expl)
13101 rc.Report.Error (8173, loc, "The expression must be of type `{0}' because it is being assigned by reference", target.GetSignatureForError ());
13105 class ByRefDereference : CompositeExpression, IMemoryLocation, IAssignMethod
13107 bool prepared;
13108 LocalTemporary temporary;
13110 private ByRefDereference (Expression expr)
13111 : base (expr)
13115 public static Expression Create (Expression expr)
13117 var rc = expr.Type as ReferenceContainer;
13118 if (rc == null)
13119 return expr;
13121 return new ByRefDereference (expr) {
13122 type = rc.Element
13126 public void AddressOf (EmitContext ec, AddressOp mode)
13128 expr.Emit (ec);
13131 public override Expression CreateExpressionTree (ResolveContext rc)
13133 rc.Report.Error (8153, Location, "An expression tree lambda cannot contain a call to a method, property, or indexer that returns by reference");
13134 return null;
13137 public void Emit (EmitContext ec, bool leave_copy)
13139 Emit (ec);
13140 if (leave_copy) {
13141 ec.Emit (OpCodes.Dup);
13142 temporary = new LocalTemporary (type);
13143 temporary.Store (ec);
13147 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
13149 prepared = isCompound;
13151 expr.Emit (ec);
13153 if (isCompound)
13154 ec.Emit (OpCodes.Dup);
13156 source.Emit (ec);
13157 if (leave_copy) {
13158 throw new NotImplementedException ("leave_copy");
13161 ec.EmitStoreFromPtr (type);
13163 if (temporary != null) {
13164 temporary.Emit (ec);
13165 temporary.Release (ec);
13169 protected override Expression DoResolve (ResolveContext rc)
13171 eclass = ExprClass.Variable;
13172 return this;
13175 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
13177 if (expr.ContainsEmitWithAwait ()) {
13178 rc.Report.Error (8178, loc, "`await' cannot be used in an expression containing a call to `{0}' because it returns by reference",
13179 expr.GetSignatureForError ());
13182 return DoResolve (rc);
13185 public override void Emit (EmitContext ec)
13187 if (!prepared)
13188 base.Emit(ec);
13190 ec.EmitLoadFromPtr (type);
13193 public override object Accept (StructuralVisitor visitor)
13195 return visitor.Visit (this);
13199 class DefaultLiteralExpression : Expression
13201 public DefaultLiteralExpression (Location loc)
13203 this.loc = loc;
13206 public override Expression CreateExpressionTree (ResolveContext ec)
13208 throw new NotImplementedException ();
13211 protected override Expression DoResolve (ResolveContext rc)
13213 type = InternalType.DefaultType;
13214 eclass = ExprClass.Value;
13215 return this;
13218 public override void Emit (EmitContext ec)
13220 throw new NotSupportedException ();
13224 class Discard : Expression, IAssignMethod, IMemoryLocation
13226 public Discard (Location loc)
13228 this.loc = loc;
13231 public override Expression CreateExpressionTree (ResolveContext rc)
13233 rc.Report.Error (8207, loc, "An expression tree cannot contain a discard");
13234 return null;
13237 protected override Expression DoResolve (ResolveContext rc)
13239 type = InternalType.Discard;
13240 eclass = ExprClass.Variable;
13241 return this;
13244 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
13246 if (right_side.Type == InternalType.DefaultType) {
13247 rc.Report.Error (8183, loc, "Cannot infer the type of implicitly-typed discard");
13248 type = InternalType.ErrorType;
13249 return this;
13252 if (right_side.Type.Kind == MemberKind.Void) {
13253 rc.Report.Error (8209, loc, "Cannot assign void to a discard");
13254 type = InternalType.ErrorType;
13255 return this;
13258 if (right_side != EmptyExpression.OutAccess) {
13259 type = right_side.Type;
13262 return this;
13265 public override void Emit (EmitContext ec)
13267 throw new NotImplementedException ();
13270 public void Emit (EmitContext ec, bool leave_copy)
13272 throw new NotImplementedException ();
13275 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
13277 if (leave_copy)
13278 source.Emit (ec);
13279 else
13280 source.EmitSideEffect (ec);
13283 public void AddressOf (EmitContext ec, AddressOp mode)
13285 var temp = ec.GetTemporaryLocal (type);
13286 ec.Emit (OpCodes.Ldloca, temp);
13288 // TODO: Should free it on next statement but don't have mechanism for that yet
13289 // ec.FreeTemporaryLocal (temp, type);