.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

10396 lines
272 KiB

//
// expression.cs: Expression representation for the IL tree.
//
// Author:
// Miguel de Icaza (miguel@ximian.com)
// Marek Safar (marek.safar@gmail.com)
//
// Copyright 2001, 2002, 2003 Ximian, Inc.
// Copyright 2003-2008 Novell, Inc.
// Copyright 2011 Xamarin Inc.
//
using System;
using System.Collections.Generic;
using System.Linq;
using SLE = System.Linq.Expressions;
#if STATIC
using MetaType = IKVM.Reflection.Type;
using IKVM.Reflection;
using IKVM.Reflection.Emit;
#else
using MetaType = System.Type;
using System.Reflection;
using System.Reflection.Emit;
#endif
namespace Mono.CSharp
{
//
// This is an user operator expression, automatically created during
// resolve phase
//
public class UserOperatorCall : Expression {
protected readonly Arguments arguments;
protected readonly MethodSpec oper;
readonly Func<ResolveContext, Expression, Expression> expr_tree;
public UserOperatorCall (MethodSpec oper, Arguments args, Func<ResolveContext, Expression, Expression> expr_tree, Location loc)
{
this.oper = oper;
this.arguments = args;
this.expr_tree = expr_tree;
type = oper.ReturnType;
eclass = ExprClass.Value;
this.loc = loc;
}
public override bool ContainsEmitWithAwait ()
{
return arguments.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
if (expr_tree != null)
return expr_tree (ec, new TypeOfMethod (oper, loc));
Arguments args = Arguments.CreateForExpressionTree (ec, arguments,
new NullLiteral (loc),
new TypeOfMethod (oper, loc));
return CreateExpressionFactoryCall (ec, "Call", args);
}
protected override void CloneTo (CloneContext context, Expression target)
{
// Nothing to clone
}
protected override Expression DoResolve (ResolveContext ec)
{
//
// We are born fully resolved
//
return this;
}
public override void Emit (EmitContext ec)
{
var call = new CallEmitter ();
call.EmitPredefined (ec, oper, arguments);
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
#if STATIC
return base.MakeExpression (ctx);
#else
return SLE.Expression.Call ((MethodInfo) oper.GetMetaInfo (), Arguments.MakeExpression (arguments, ctx));
#endif
}
}
public class ParenthesizedExpression : ShimExpression
{
public ParenthesizedExpression (Expression expr)
: base (expr)
{
loc = expr.Location;
}
protected override Expression DoResolve (ResolveContext ec)
{
return expr.Resolve (ec);
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
return expr.DoResolveLValue (ec, right_side);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
//
// Unary implements unary expressions.
//
public class Unary : Expression
{
public enum Operator : byte {
UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
AddressOf, TOP
}
public readonly Operator Oper;
public Expression Expr;
Expression enum_conversion;
public Unary (Operator op, Expression expr, Location loc)
{
Oper = op;
Expr = expr;
this.loc = loc;
}
// <summary>
// This routine will attempt to simplify the unary expression when the
// argument is a constant.
// </summary>
Constant TryReduceConstant (ResolveContext ec, Constant e)
{
if (e is EmptyConstantCast)
return TryReduceConstant (ec, ((EmptyConstantCast) e).child);
if (e is SideEffectConstant) {
Constant r = TryReduceConstant (ec, ((SideEffectConstant) e).value);
return r == null ? null : new SideEffectConstant (r, e, r.Location);
}
TypeSpec expr_type = e.Type;
switch (Oper){
case Operator.UnaryPlus:
// Unary numeric promotions
switch (expr_type.BuiltinType) {
case BuiltinTypeSpec.Type.Byte:
return new IntConstant (ec.BuiltinTypes, ((ByteConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.SByte:
return new IntConstant (ec.BuiltinTypes, ((SByteConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.Short:
return new IntConstant (ec.BuiltinTypes, ((ShortConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.UShort:
return new IntConstant (ec.BuiltinTypes, ((UShortConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.Char:
return new IntConstant (ec.BuiltinTypes, ((CharConstant) e).Value, e.Location);
// Predefined operators
case BuiltinTypeSpec.Type.Int:
case BuiltinTypeSpec.Type.UInt:
case BuiltinTypeSpec.Type.Long:
case BuiltinTypeSpec.Type.ULong:
case BuiltinTypeSpec.Type.Float:
case BuiltinTypeSpec.Type.Double:
case BuiltinTypeSpec.Type.Decimal:
return e;
}
return null;
case Operator.UnaryNegation:
// Unary numeric promotions
switch (expr_type.BuiltinType) {
case BuiltinTypeSpec.Type.Byte:
return new IntConstant (ec.BuiltinTypes, -((ByteConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.SByte:
return new IntConstant (ec.BuiltinTypes, -((SByteConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.Short:
return new IntConstant (ec.BuiltinTypes, -((ShortConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.UShort:
return new IntConstant (ec.BuiltinTypes, -((UShortConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.Char:
return new IntConstant (ec.BuiltinTypes, -((CharConstant) e).Value, e.Location);
// Predefined operators
case BuiltinTypeSpec.Type.Int:
int ivalue = ((IntConstant) e).Value;
if (ivalue == int.MinValue) {
if (ec.ConstantCheckState) {
ConstantFold.Error_CompileTimeOverflow (ec, loc);
return null;
}
return e;
}
return new IntConstant (ec.BuiltinTypes, -ivalue, e.Location);
case BuiltinTypeSpec.Type.Long:
long lvalue = ((LongConstant) e).Value;
if (lvalue == long.MinValue) {
if (ec.ConstantCheckState) {
ConstantFold.Error_CompileTimeOverflow (ec, loc);
return null;
}
return e;
}
return new LongConstant (ec.BuiltinTypes, -lvalue, e.Location);
case BuiltinTypeSpec.Type.UInt:
UIntLiteral uil = e as UIntLiteral;
if (uil != null) {
if (uil.Value == int.MaxValue + (uint) 1)
return new IntLiteral (ec.BuiltinTypes, int.MinValue, e.Location);
return new LongLiteral (ec.BuiltinTypes, -uil.Value, e.Location);
}
return new LongConstant (ec.BuiltinTypes, -((UIntConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.ULong:
ULongLiteral ull = e as ULongLiteral;
if (ull != null && ull.Value == 9223372036854775808)
return new LongLiteral (ec.BuiltinTypes, long.MinValue, e.Location);
return null;
case BuiltinTypeSpec.Type.Float:
FloatLiteral fl = e as FloatLiteral;
// For better error reporting
if (fl != null)
return new FloatLiteral (ec.BuiltinTypes, -fl.Value, e.Location);
return new FloatConstant (ec.BuiltinTypes, -((FloatConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.Double:
DoubleLiteral dl = e as DoubleLiteral;
// For better error reporting
if (dl != null)
return new DoubleLiteral (ec.BuiltinTypes, -dl.Value, e.Location);
return new DoubleConstant (ec.BuiltinTypes, -((DoubleConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.Decimal:
return new DecimalConstant (ec.BuiltinTypes, -((DecimalConstant) e).Value, e.Location);
}
return null;
case Operator.LogicalNot:
if (expr_type.BuiltinType != BuiltinTypeSpec.Type.Bool)
return null;
bool b = (bool)e.GetValue ();
return new BoolConstant (ec.BuiltinTypes, !b, e.Location);
case Operator.OnesComplement:
// Unary numeric promotions
switch (expr_type.BuiltinType) {
case BuiltinTypeSpec.Type.Byte:
return new IntConstant (ec.BuiltinTypes, ~((ByteConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.SByte:
return new IntConstant (ec.BuiltinTypes, ~((SByteConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.Short:
return new IntConstant (ec.BuiltinTypes, ~((ShortConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.UShort:
return new IntConstant (ec.BuiltinTypes, ~((UShortConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.Char:
return new IntConstant (ec.BuiltinTypes, ~((CharConstant) e).Value, e.Location);
// Predefined operators
case BuiltinTypeSpec.Type.Int:
return new IntConstant (ec.BuiltinTypes, ~((IntConstant)e).Value, e.Location);
case BuiltinTypeSpec.Type.UInt:
return new UIntConstant (ec.BuiltinTypes, ~((UIntConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.Long:
return new LongConstant (ec.BuiltinTypes, ~((LongConstant) e).Value, e.Location);
case BuiltinTypeSpec.Type.ULong:
return new ULongConstant (ec.BuiltinTypes, ~((ULongConstant) e).Value, e.Location);
}
if (e is EnumConstant) {
e = TryReduceConstant (ec, ((EnumConstant)e).Child);
if (e != null)
e = new EnumConstant (e, expr_type);
return e;
}
return null;
}
throw new Exception ("Can not constant fold: " + Oper.ToString());
}
protected virtual Expression ResolveOperator (ResolveContext ec, Expression expr)
{
eclass = ExprClass.Value;
TypeSpec expr_type = expr.Type;
Expression best_expr;
TypeSpec[] predefined = ec.BuiltinTypes.OperatorsUnary [(int) Oper];
//
// Primitive types first
//
if (BuiltinTypeSpec.IsPrimitiveType (expr_type)) {
best_expr = ResolvePrimitivePredefinedType (ec, expr, predefined);
if (best_expr == null)
return null;
type = best_expr.Type;
Expr = best_expr;
return this;
}
//
// E operator ~(E x);
//
if (Oper == Operator.OnesComplement && expr_type.IsEnum)
return ResolveEnumOperator (ec, expr, predefined);
return ResolveUserType (ec, expr, predefined);
}
protected virtual Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
{
TypeSpec underlying_type = EnumSpec.GetUnderlyingType (expr.Type);
Expression best_expr = ResolvePrimitivePredefinedType (ec, EmptyCast.Create (expr, underlying_type), predefined);
if (best_expr == null)
return null;
Expr = best_expr;
enum_conversion = Convert.ExplicitNumericConversion (ec, new EmptyExpression (best_expr.Type), underlying_type);
type = expr.Type;
return EmptyCast.Create (this, type);
}
public override bool ContainsEmitWithAwait ()
{
return Expr.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
return CreateExpressionTree (ec, null);
}
Expression CreateExpressionTree (ResolveContext ec, Expression user_op)
{
string method_name;
switch (Oper) {
case Operator.AddressOf:
Error_PointerInsideExpressionTree (ec);
return null;
case Operator.UnaryNegation:
if (ec.HasSet (ResolveContext.Options.CheckedScope) && user_op == null && !IsFloat (type))
method_name = "NegateChecked";
else
method_name = "Negate";
break;
case Operator.OnesComplement:
case Operator.LogicalNot:
method_name = "Not";
break;
case Operator.UnaryPlus:
method_name = "UnaryPlus";
break;
default:
throw new InternalErrorException ("Unknown unary operator " + Oper.ToString ());
}
Arguments args = new Arguments (2);
args.Add (new Argument (Expr.CreateExpressionTree (ec)));
if (user_op != null)
args.Add (new Argument (user_op));
return CreateExpressionFactoryCall (ec, method_name, args);
}
public static TypeSpec[][] CreatePredefinedOperatorsTable (BuiltinTypes types)
{
var predefined_operators = new TypeSpec[(int) Operator.TOP][];
//
// 7.6.1 Unary plus operator
//
predefined_operators [(int) Operator.UnaryPlus] = new TypeSpec [] {
types.Int, types.UInt,
types.Long, types.ULong,
types.Float, types.Double,
types.Decimal
};
//
// 7.6.2 Unary minus operator
//
predefined_operators [(int) Operator.UnaryNegation] = new TypeSpec [] {
types.Int, types.Long,
types.Float, types.Double,
types.Decimal
};
//
// 7.6.3 Logical negation operator
//
predefined_operators [(int) Operator.LogicalNot] = new TypeSpec [] {
types.Bool
};
//
// 7.6.4 Bitwise complement operator
//
predefined_operators [(int) Operator.OnesComplement] = new TypeSpec [] {
types.Int, types.UInt,
types.Long, types.ULong
};
return predefined_operators;
}
//
// Unary numeric promotions
//
static Expression DoNumericPromotion (ResolveContext rc, Operator op, Expression expr)
{
TypeSpec expr_type = expr.Type;
if (op == Operator.UnaryPlus || op == Operator.UnaryNegation || op == Operator.OnesComplement) {
switch (expr_type.BuiltinType) {
case BuiltinTypeSpec.Type.Byte:
case BuiltinTypeSpec.Type.SByte:
case BuiltinTypeSpec.Type.Short:
case BuiltinTypeSpec.Type.UShort:
case BuiltinTypeSpec.Type.Char:
return Convert.ImplicitNumericConversion (expr, rc.BuiltinTypes.Int);
}
}
if (op == Operator.UnaryNegation && expr_type.BuiltinType == BuiltinTypeSpec.Type.UInt)
return Convert.ImplicitNumericConversion (expr, rc.BuiltinTypes.Long);
return expr;
}
protected override Expression DoResolve (ResolveContext ec)
{
if (Oper == Operator.AddressOf) {
return ResolveAddressOf (ec);
}
Expr = Expr.Resolve (ec);
if (Expr == null)
return null;
if (Expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
Arguments args = new Arguments (1);
args.Add (new Argument (Expr));
return new DynamicUnaryConversion (GetOperatorExpressionTypeName (), args, loc).Resolve (ec);
}
if (Expr.Type.IsNullableType)
return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
//
// Attempt to use a constant folding operation.
//
Constant cexpr = Expr as Constant;
if (cexpr != null) {
cexpr = TryReduceConstant (ec, cexpr);
if (cexpr != null)
return cexpr;
}
Expression expr = ResolveOperator (ec, Expr);
if (expr == null)
Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type);
//
// Reduce unary operator on predefined types
//
if (expr == this && Oper == Operator.UnaryPlus)
return Expr;
return expr;
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right)
{
return null;
}
public override void Emit (EmitContext ec)
{
EmitOperator (ec, type);
}
protected void EmitOperator (EmitContext ec, TypeSpec type)
{
switch (Oper) {
case Operator.UnaryPlus:
Expr.Emit (ec);
break;
case Operator.UnaryNegation:
if (ec.HasSet (EmitContext.Options.CheckedScope) && !IsFloat (type)) {
if (ec.HasSet (BuilderContext.Options.AsyncBody) && Expr.ContainsEmitWithAwait ())
Expr = Expr.EmitToField (ec);
ec.EmitInt (0);
if (type.BuiltinType == BuiltinTypeSpec.Type.Long)
ec.Emit (OpCodes.Conv_U8);
Expr.Emit (ec);
ec.Emit (OpCodes.Sub_Ovf);
} else {
Expr.Emit (ec);
ec.Emit (OpCodes.Neg);
}
break;
case Operator.LogicalNot:
Expr.Emit (ec);
ec.EmitInt (0);
ec.Emit (OpCodes.Ceq);
break;
case Operator.OnesComplement:
Expr.Emit (ec);
ec.Emit (OpCodes.Not);
break;
case Operator.AddressOf:
((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
break;
default:
throw new Exception ("This should not happen: Operator = "
+ Oper.ToString ());
}
//
// Same trick as in Binary expression
//
if (enum_conversion != null)
enum_conversion.Emit (ec);
}
public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
{
if (Oper == Operator.LogicalNot)
Expr.EmitBranchable (ec, target, !on_true);
else
base.EmitBranchable (ec, target, on_true);
}
public override void EmitSideEffect (EmitContext ec)
{
Expr.EmitSideEffect (ec);
}
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
string GetOperatorExpressionTypeName ()
{
switch (Oper) {
case Operator.OnesComplement:
return "OnesComplement";
case Operator.LogicalNot:
return "Not";
case Operator.UnaryNegation:
return "Negate";
case Operator.UnaryPlus:
return "UnaryPlus";
default:
throw new NotImplementedException ("Unknown express type operator " + Oper.ToString ());
}
}
static bool IsFloat (TypeSpec t)
{
return t.BuiltinType == BuiltinTypeSpec.Type.Double || t.BuiltinType == BuiltinTypeSpec.Type.Float;
}
//
// Returns a stringified representation of the Operator
//
public static string OperName (Operator oper)
{
switch (oper) {
case Operator.UnaryPlus:
return "+";
case Operator.UnaryNegation:
return "-";
case Operator.LogicalNot:
return "!";
case Operator.OnesComplement:
return "~";
case Operator.AddressOf:
return "&";
}
throw new NotImplementedException (oper.ToString ());
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
var expr = Expr.MakeExpression (ctx);
bool is_checked = ctx.HasSet (BuilderContext.Options.CheckedScope);
switch (Oper) {
case Operator.UnaryNegation:
return is_checked ? SLE.Expression.NegateChecked (expr) : SLE.Expression.Negate (expr);
case Operator.LogicalNot:
return SLE.Expression.Not (expr);
#if NET_4_0 || MONODROID
case Operator.OnesComplement:
return SLE.Expression.OnesComplement (expr);
#endif
default:
throw new NotImplementedException (Oper.ToString ());
}
}
Expression ResolveAddressOf (ResolveContext ec)
{
if (!ec.IsUnsafe)
UnsafeError (ec, loc);
Expr = Expr.DoResolveLValue (ec, EmptyExpression.UnaryAddress);
if (Expr == null || Expr.eclass != ExprClass.Variable) {
ec.Report.Error (211, loc, "Cannot take the address of the given expression");
return null;
}
if (!TypeManager.VerifyUnmanaged (ec.Module, Expr.Type, loc)) {
return null;
}
IVariableReference vr = Expr as IVariableReference;
bool is_fixed;
if (vr != null) {
is_fixed = vr.IsFixed;
vr.SetHasAddressTaken ();
if (vr.IsHoisted) {
AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, vr, loc);
}
} else {
IFixedExpression fe = Expr as IFixedExpression;
is_fixed = fe != null && fe.IsFixed;
}
if (!is_fixed && !ec.HasSet (ResolveContext.Options.FixedInitializerScope)) {
ec.Report.Error (212, loc, "You can only take the address of unfixed expression inside of a fixed statement initializer");
}
type = PointerContainer.MakeType (ec.Module, Expr.Type);
eclass = ExprClass.Value;
return this;
}
Expression ResolvePrimitivePredefinedType (ResolveContext rc, Expression expr, TypeSpec[] predefined)
{
expr = DoNumericPromotion (rc, Oper, expr);
TypeSpec expr_type = expr.Type;
foreach (TypeSpec t in predefined) {
if (t == expr_type)
return expr;
}
return null;
}
//
// Perform user-operator overload resolution
//
protected virtual Expression ResolveUserOperator (ResolveContext ec, Expression expr)
{
CSharp.Operator.OpType op_type;
switch (Oper) {
case Operator.LogicalNot:
op_type = CSharp.Operator.OpType.LogicalNot; break;
case Operator.OnesComplement:
op_type = CSharp.Operator.OpType.OnesComplement; break;
case Operator.UnaryNegation:
op_type = CSharp.Operator.OpType.UnaryNegation; break;
case Operator.UnaryPlus:
op_type = CSharp.Operator.OpType.UnaryPlus; break;
default:
throw new InternalErrorException (Oper.ToString ());
}
var methods = MemberCache.GetUserOperator (expr.Type, op_type, false);
if (methods == null)
return null;
Arguments args = new Arguments (1);
args.Add (new Argument (expr));
var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc);
var oper = res.ResolveOperator (ec, ref args);
if (oper == null)
return null;
Expr = args [0].Expr;
return new UserOperatorCall (oper, args, CreateExpressionTree, expr.Location);
}
//
// Unary user type overload resolution
//
Expression ResolveUserType (ResolveContext ec, Expression expr, TypeSpec[] predefined)
{
Expression best_expr = ResolveUserOperator (ec, expr);
if (best_expr != null)
return best_expr;
foreach (TypeSpec t in predefined) {
Expression oper_expr = Convert.ImplicitUserConversion (ec, expr, t, expr.Location);
if (oper_expr == null)
continue;
if (oper_expr == ErrorExpression.Instance)
return oper_expr;
//
// decimal type is predefined but has user-operators
//
if (oper_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal)
oper_expr = ResolveUserType (ec, oper_expr, predefined);
else
oper_expr = ResolvePrimitivePredefinedType (ec, oper_expr, predefined);
if (oper_expr == null)
continue;
if (best_expr == null) {
best_expr = oper_expr;
continue;
}
int result = OverloadResolver.BetterTypeConversion (ec, best_expr.Type, t);
if (result == 0) {
if ((oper_expr is UserOperatorCall || oper_expr is UserCast) && (best_expr is UserOperatorCall || best_expr is UserCast)) {
ec.Report.Error (35, loc, "Operator `{0}' is ambiguous on an operand of type `{1}'",
OperName (Oper), expr.Type.GetSignatureForError ());
} else {
Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), expr.Type);
}
break;
}
if (result == 2)
best_expr = oper_expr;
}
if (best_expr == null)
return null;
//
// HACK: Decimal user-operator is included in standard operators
//
if (best_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal)
return best_expr;
Expr = best_expr;
type = best_expr.Type;
return this;
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Unary target = (Unary) t;
target.Expr = Expr.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
//
// Unary operators are turned into Indirection expressions
// after semantic analysis (this is so we can take the address
// of an indirection).
//
public class Indirection : Expression, IMemoryLocation, IAssignMethod, IFixedExpression {
Expression expr;
LocalTemporary temporary;
bool prepared;
public Indirection (Expression expr, Location l)
{
this.expr = expr;
loc = l;
}
public Expression Expr {
get {
return expr;
}
}
public bool IsFixed {
get { return true; }
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Indirection target = (Indirection) t;
target.expr = expr.Clone (clonectx);
}
public override bool ContainsEmitWithAwait ()
{
throw new NotImplementedException ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Error_PointerInsideExpressionTree (ec);
return null;
}
public override void Emit (EmitContext ec)
{
if (!prepared)
expr.Emit (ec);
ec.EmitLoadFromPtr (Type);
}
public void Emit (EmitContext ec, bool leave_copy)
{
Emit (ec);
if (leave_copy) {
ec.Emit (OpCodes.Dup);
temporary = new LocalTemporary (expr.Type);
temporary.Store (ec);
}
}
public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
{
prepared = isCompound;
expr.Emit (ec);
if (isCompound)
ec.Emit (OpCodes.Dup);
source.Emit (ec);
if (leave_copy) {
ec.Emit (OpCodes.Dup);
temporary = new LocalTemporary (source.Type);
temporary.Store (ec);
}
ec.EmitStoreFromPtr (type);
if (temporary != null) {
temporary.Emit (ec);
temporary.Release (ec);
}
}
public void AddressOf (EmitContext ec, AddressOp Mode)
{
expr.Emit (ec);
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
return DoResolve (ec);
}
protected override Expression DoResolve (ResolveContext ec)
{
expr = expr.Resolve (ec);
if (expr == null)
return null;
if (!ec.IsUnsafe)
UnsafeError (ec, loc);
var pc = expr.Type as PointerContainer;
if (pc == null) {
ec.Report.Error (193, loc, "The * or -> operator must be applied to a pointer");
return null;
}
type = pc.Element;
if (type.Kind == MemberKind.Void) {
Error_VoidPointerOperation (ec);
return null;
}
eclass = ExprClass.Variable;
return this;
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Unary Mutator expressions (pre and post ++ and --)
/// </summary>
///
/// <remarks>
/// UnaryMutator implements ++ and -- expressions. It derives from
/// ExpressionStatement becuase the pre/post increment/decrement
/// operators can be used in a statement context.
///
/// FIXME: Idea, we could split this up in two classes, one simpler
/// for the common case, and one with the extra fields for more complex
/// classes (indexers require temporary access; overloaded require method)
///
/// </remarks>
public class UnaryMutator : ExpressionStatement
{
class DynamicPostMutator : Expression, IAssignMethod
{
LocalTemporary temp;
Expression expr;
public DynamicPostMutator (Expression expr)
{
this.expr = expr;
this.type = expr.Type;
this.loc = expr.Location;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
throw new NotImplementedException ("ET");
}
protected override Expression DoResolve (ResolveContext rc)
{
eclass = expr.eclass;
return this;
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
expr.DoResolveLValue (ec, right_side);
return DoResolve (ec);
}
public override void Emit (EmitContext ec)
{
temp.Emit (ec);
}
public void Emit (EmitContext ec, bool leave_copy)
{
throw new NotImplementedException ();
}
//
// Emits target assignment using unmodified source value
//
public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
{
//
// Allocate temporary variable to keep original value before it's modified
//
temp = new LocalTemporary (type);
expr.Emit (ec);
temp.Store (ec);
((IAssignMethod) expr).EmitAssign (ec, source, false, isCompound);
if (leave_copy)
Emit (ec);
temp.Release (ec);
temp = null;
}
}
[Flags]
public enum Mode : byte {
IsIncrement = 0,
IsDecrement = 1,
IsPre = 0,
IsPost = 2,
PreIncrement = 0,
PreDecrement = IsDecrement,
PostIncrement = IsPost,
PostDecrement = IsPost | IsDecrement
}
Mode mode;
bool is_expr, recurse;
protected Expression expr;
// Holds the real operation
Expression operation;
public UnaryMutator (Mode m, Expression e, Location loc)
{
mode = m;
this.loc = loc;
expr = e;
}
public Mode UnaryMutatorMode {
get {
return mode;
}
}
public Expression Expr {
get {
return expr;
}
}
public override bool ContainsEmitWithAwait ()
{
return expr.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
return new SimpleAssign (this, this).CreateExpressionTree (ec);
}
public static TypeSpec[] CreatePredefinedOperatorsTable (BuiltinTypes types)
{
//
// Predefined ++ and -- operators exist for the following types:
// sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal
//
return new TypeSpec[] {
types.Int,
types.Long,
types.SByte,
types.Byte,
types.Short,
types.UInt,
types.ULong,
types.Char,
types.Float,
types.Double,
types.Decimal
};
}
protected override Expression DoResolve (ResolveContext ec)
{
expr = expr.Resolve (ec);
if (expr == null)
return null;
if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
//
// Handle postfix unary operators using local
// temporary variable
//
if ((mode & Mode.IsPost) != 0)
expr = new DynamicPostMutator (expr);
Arguments args = new Arguments (1);
args.Add (new Argument (expr));
return new SimpleAssign (expr, new DynamicUnaryConversion (GetOperatorExpressionTypeName (), args, loc)).Resolve (ec);
}
if (expr.Type.IsNullableType)
return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
return DoResolveOperation (ec);
}
protected Expression DoResolveOperation (ResolveContext ec)
{
eclass = ExprClass.Value;
type = expr.Type;
if (expr is RuntimeValueExpression) {
operation = expr;
} else {
// Use itself at the top of the stack
operation = new EmptyExpression (type);
}
//
// The operand of the prefix/postfix increment decrement operators
// should be an expression that is classified as a variable,
// a property access or an indexer access
//
// TODO: Move to parser, expr is ATypeNameExpression
if (expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess) {
expr = expr.ResolveLValue (ec, expr);
} else {
ec.Report.Error (1059, loc, "The operand of an increment or decrement operator must be a variable, property or indexer");
}
//
// Step 1: Try to find a user operator, it has priority over predefined ones
//
var user_op = IsDecrement ? Operator.OpType.Decrement : Operator.OpType.Increment;
var methods = MemberCache.GetUserOperator (type, user_op, false);
if (methods != null) {
Arguments args = new Arguments (1);
args.Add (new Argument (expr));
var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc);
var method = res.ResolveOperator (ec, ref args);
if (method == null)
return null;
args[0].Expr = operation;
operation = new UserOperatorCall (method, args, null, loc);
operation = Convert.ImplicitConversionRequired (ec, operation, type, loc);
return this;
}
//
// Step 2: Try predefined types
//
Expression source = null;
bool primitive_type;
//
// Predefined without user conversion first for speed-up
//
// Predefined ++ and -- operators exist for the following types:
// sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal
//
switch (type.BuiltinType) {
case BuiltinTypeSpec.Type.Byte:
case BuiltinTypeSpec.Type.SByte:
case BuiltinTypeSpec.Type.Short:
case BuiltinTypeSpec.Type.UShort:
case BuiltinTypeSpec.Type.Int:
case BuiltinTypeSpec.Type.UInt:
case BuiltinTypeSpec.Type.Long:
case BuiltinTypeSpec.Type.ULong:
case BuiltinTypeSpec.Type.Char:
case BuiltinTypeSpec.Type.Float:
case BuiltinTypeSpec.Type.Double:
case BuiltinTypeSpec.Type.Decimal:
source = operation;
primitive_type = true;
break;
default:
primitive_type = false;
// ++/-- on pointer variables of all types except void*
if (type.IsPointer) {
if (((PointerContainer) type).Element.Kind == MemberKind.Void) {
Error_VoidPointerOperation (ec);
return null;
}
source = operation;
} else {
foreach (var t in ec.BuiltinTypes.OperatorsUnaryMutator) {
source = Convert.ImplicitUserConversion (ec, operation, t, loc);
// LAMESPEC: It should error on ambiguous operators but that would make us incompatible
if (source != null) {
break;
}
}
}
// ++/-- on enum types
if (source == null && type.IsEnum)
source = operation;
if (source == null) {
expr.Error_OperatorCannotBeApplied (ec, loc, Operator.GetName (user_op), type);
return null;
}
break;
}
var one = new IntConstant (ec.BuiltinTypes, 1, loc);
var op = IsDecrement ? Binary.Operator.Subtraction : Binary.Operator.Addition;
operation = new Binary (op, source, one, loc);
operation = operation.Resolve (ec);
if (operation == null)
throw new NotImplementedException ("should not be reached");
if (operation.Type != type) {
if (primitive_type)
operation = Convert.ExplicitNumericConversion (ec, operation, type);
else
operation = Convert.ImplicitConversionRequired (ec, operation, type, loc);
}
return this;
}
void EmitCode (EmitContext ec, bool is_expr)
{
recurse = true;
this.is_expr = is_expr;
((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
}
public override void Emit (EmitContext ec)
{
//
// We use recurse to allow ourselfs to be the source
// of an assignment. This little hack prevents us from
// having to allocate another expression
//
if (recurse) {
((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
EmitOperation (ec);
recurse = false;
return;
}
EmitCode (ec, true);
}
protected virtual void EmitOperation (EmitContext ec)
{
operation.Emit (ec);
}
public override void EmitStatement (EmitContext ec)
{
EmitCode (ec, false);
}
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
string GetOperatorExpressionTypeName ()
{
return IsDecrement ? "Decrement" : "Increment";
}
bool IsDecrement {
get { return (mode & Mode.IsDecrement) != 0; }
}
#if NET_4_0 || MONODROID
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
var target = ((RuntimeValueExpression) expr).MetaObject.Expression;
var source = SLE.Expression.Convert (operation.MakeExpression (ctx), target.Type);
return SLE.Expression.Assign (target, source);
}
#endif
protected override void CloneTo (CloneContext clonectx, Expression t)
{
UnaryMutator target = (UnaryMutator) t;
target.expr = expr.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
//
// Base class for the `is' and `as' operators
//
public abstract class Probe : Expression
{
public Expression ProbeType;
protected Expression expr;
protected TypeSpec probe_type_expr;
public Probe (Expression expr, Expression probe_type, Location l)
{
ProbeType = probe_type;
loc = l;
this.expr = expr;
}
public Expression Expr {
get {
return expr;
}
}
public override bool ContainsEmitWithAwait ()
{
return expr.ContainsEmitWithAwait ();
}
protected override Expression DoResolve (ResolveContext ec)
{
probe_type_expr = ProbeType.ResolveAsType (ec);
if (probe_type_expr == null)
return null;
expr = expr.Resolve (ec);
if (expr == null)
return null;
if (probe_type_expr.IsStatic) {
ec.Report.Error (-244, loc, "The `{0}' operator cannot be applied to an operand of a static type",
OperatorName);
}
if (expr.Type.IsPointer || probe_type_expr.IsPointer) {
ec.Report.Error (244, loc, "The `{0}' operator cannot be applied to an operand of pointer type",
OperatorName);
return null;
}
if (expr.Type == InternalType.AnonymousMethod) {
ec.Report.Error (837, loc, "The `{0}' operator cannot be applied to a lambda expression or anonymous method",
OperatorName);
return null;
}
return this;
}
protected abstract string OperatorName { get; }
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Probe target = (Probe) t;
target.expr = expr.Clone (clonectx);
target.ProbeType = ProbeType.Clone (clonectx);
}
}
/// <summary>
/// Implementation of the `is' operator.
/// </summary>
public class Is : Probe
{
Nullable.Unwrap expr_unwrap;
public Is (Expression expr, Expression probe_type, Location l)
: base (expr, probe_type, l)
{
}
protected override string OperatorName {
get { return "is"; }
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = Arguments.CreateForExpressionTree (ec, null,
expr.CreateExpressionTree (ec),
new TypeOf (probe_type_expr, loc));
return CreateExpressionFactoryCall (ec, "TypeIs", args);
}
public override void Emit (EmitContext ec)
{
if (expr_unwrap != null) {
expr_unwrap.EmitCheck (ec);
return;
}
expr.Emit (ec);
// Only to make verifier happy
if (probe_type_expr.IsGenericParameter && TypeSpec.IsValueType (expr.Type))
ec.Emit (OpCodes.Box, expr.Type);
ec.Emit (OpCodes.Isinst, probe_type_expr);
ec.EmitNull ();
ec.Emit (OpCodes.Cgt_Un);
}
public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
{
if (expr_unwrap != null) {
expr_unwrap.EmitCheck (ec);
} else {
expr.Emit (ec);
ec.Emit (OpCodes.Isinst, probe_type_expr);
}
ec.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target);
}
Expression CreateConstantResult (ResolveContext ec, bool result)
{
if (result)
ec.Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
TypeManager.CSharpName (probe_type_expr));
else
ec.Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
TypeManager.CSharpName (probe_type_expr));
return ReducedExpression.Create (new BoolConstant (ec.BuiltinTypes, result, loc), this);
}
protected override Expression DoResolve (ResolveContext ec)
{
if (base.DoResolve (ec) == null)
return null;
TypeSpec d = expr.Type;
bool d_is_nullable = false;
//
// If E is a method group or the null literal, or if the type of E is a reference
// type or a nullable type and the value of E is null, the result is false
//
if (expr.IsNull || expr.eclass == ExprClass.MethodGroup)
return CreateConstantResult (ec, false);
if (d.IsNullableType) {
var ut = Nullable.NullableInfo.GetUnderlyingType (d);
if (!ut.IsGenericParameter) {
d = ut;
d_is_nullable = true;
}
}
type = ec.BuiltinTypes.Bool;
eclass = ExprClass.Value;
TypeSpec t = probe_type_expr;
bool t_is_nullable = false;
if (t.IsNullableType) {
var ut = Nullable.NullableInfo.GetUnderlyingType (t);
if (!ut.IsGenericParameter) {
t = ut;
t_is_nullable = true;
}
}
if (t.IsStruct) {
if (d == t) {
//
// D and T are the same value types but D can be null
//
if (d_is_nullable && !t_is_nullable) {
expr_unwrap = Nullable.Unwrap.Create (expr, false);
return this;
}
//
// The result is true if D and T are the same value types
//
return CreateConstantResult (ec, true);
}
var tp = d as TypeParameterSpec;
if (tp != null)
return ResolveGenericParameter (ec, t, tp);
//
// An unboxing conversion exists
//
if (Convert.ExplicitReferenceConversionExists (d, t))
return this;
} else {
if (TypeManager.IsGenericParameter (t))
return ResolveGenericParameter (ec, d, (TypeParameterSpec) t);
if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
ec.Report.Warning (1981, 3, loc,
"Using `{0}' to test compatibility with `{1}' is identical to testing compatibility with `object'",
OperatorName, t.GetSignatureForError ());
}
if (TypeManager.IsGenericParameter (d))
return ResolveGenericParameter (ec, t, (TypeParameterSpec) d);
if (TypeSpec.IsValueType (d)) {
if (Convert.ImplicitBoxingConversion (null, d, t) != null) {
if (d_is_nullable && !t_is_nullable) {
expr_unwrap = Nullable.Unwrap.Create (expr, false);
return this;
}
return CreateConstantResult (ec, true);
}
} else {
if (Convert.ImplicitReferenceConversionExists (d, t)) {
//
// Do not optimize for imported type
//
if (d.MemberDefinition.IsImported && d.BuiltinType != BuiltinTypeSpec.Type.None)
return this;
//
// Turn is check into simple null check for implicitly convertible reference types
//
return ReducedExpression.Create (
new Binary (Binary.Operator.Inequality, expr, new NullLiteral (loc), loc).Resolve (ec),
this).Resolve (ec);
}
if (Convert.ExplicitReferenceConversionExists (d, t)) {
return this;
}
}
}
return CreateConstantResult (ec, false);
}
Expression ResolveGenericParameter (ResolveContext ec, TypeSpec d, TypeParameterSpec t)
{
if (t.IsReferenceType) {
if (d.IsStruct)
return CreateConstantResult (ec, false);
}
if (TypeManager.IsGenericParameter (expr.Type)) {
if (expr.Type == d && TypeSpec.IsValueType (t))
return CreateConstantResult (ec, true);
expr = new BoxedCast (expr, d);
}
return this;
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Implementation of the `as' operator.
/// </summary>
public class As : Probe {
Expression resolved_type;
public As (Expression expr, Expression probe_type, Location l)
: base (expr, probe_type, l)
{
}
protected override string OperatorName {
get { return "as"; }
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = Arguments.CreateForExpressionTree (ec, null,
expr.CreateExpressionTree (ec),
new TypeOf (probe_type_expr, loc));
return CreateExpressionFactoryCall (ec, "TypeAs", args);
}
public override void Emit (EmitContext ec)
{
expr.Emit (ec);
ec.Emit (OpCodes.Isinst, type);
if (TypeManager.IsGenericParameter (type) || type.IsNullableType)
ec.Emit (OpCodes.Unbox_Any, type);
}
protected override Expression DoResolve (ResolveContext ec)
{
if (resolved_type == null) {
resolved_type = base.DoResolve (ec);
if (resolved_type == null)
return null;
}
type = probe_type_expr;
eclass = ExprClass.Value;
TypeSpec etype = expr.Type;
if (!TypeSpec.IsReferenceType (type) && !type.IsNullableType) {
if (TypeManager.IsGenericParameter (type)) {
ec.Report.Error (413, loc,
"The `as' operator cannot be used with a non-reference type parameter `{0}'. Consider adding `class' or a reference type constraint",
probe_type_expr.GetSignatureForError ());
} else {
ec.Report.Error (77, loc,
"The `as' operator cannot be used with a non-nullable value type `{0}'",
TypeManager.CSharpName (type));
}
return null;
}
if (expr.IsNull && type.IsNullableType) {
return Nullable.LiftedNull.CreateFromExpression (ec, this);
}
// If the compile-time type of E is dynamic, unlike the cast operator the as operator is not dynamically bound
if (etype.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
return this;
}
Expression e = Convert.ImplicitConversionStandard (ec, expr, type, loc);
if (e != null) {
e = EmptyCast.Create (e, type);
return ReducedExpression.Create (e, this).Resolve (ec);
}
if (Convert.ExplicitReferenceConversionExists (etype, type)){
if (TypeManager.IsGenericParameter (etype))
expr = new BoxedCast (expr, etype);
return this;
}
if (InflatedTypeSpec.ContainsTypeParameter (etype) || InflatedTypeSpec.ContainsTypeParameter (type)) {
expr = new BoxedCast (expr, etype);
return this;
}
ec.Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
TypeManager.CSharpName (etype), TypeManager.CSharpName (type));
return null;
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
//
// This represents a typecast in the source language.
//
public class Cast : ShimExpression {
Expression target_type;
public Cast (Expression cast_type, Expression expr, Location loc)
: base (expr)
{
this.target_type = cast_type;
this.loc = loc;
}
public Expression TargetType {
get { return target_type; }
}
protected override Expression DoResolve (ResolveContext ec)
{
expr = expr.Resolve (ec);
if (expr == null)
return null;
type = target_type.ResolveAsType (ec);
if (type == null)
return null;
if (type.IsStatic) {
ec.Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
return null;
}
eclass = ExprClass.Value;
Constant c = expr as Constant;
if (c != null) {
c = c.TryReduce (ec, type, loc);
if (c != null)
return c;
}
if (type.IsPointer && !ec.IsUnsafe) {
UnsafeError (ec, loc);
}
var res = Convert.ExplicitConversion (ec, expr, type, loc);
if (res == expr)
return EmptyCast.Create (res, type);
return res;
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Cast target = (Cast) t;
target.target_type = target_type.Clone (clonectx);
target.expr = expr.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public class ImplicitCast : ShimExpression
{
bool arrayAccess;
public ImplicitCast (Expression expr, TypeSpec target, bool arrayAccess)
: base (expr)
{
this.loc = expr.Location;
this.type = target;
this.arrayAccess = arrayAccess;
}
protected override Expression DoResolve (ResolveContext ec)
{
expr = expr.Resolve (ec);
if (expr == null)
return null;
if (arrayAccess)
expr = ConvertExpressionToArrayIndex (ec, expr);
else
expr = Convert.ImplicitConversionRequired (ec, expr, type, loc);
return expr;
}
}
//
// C# 2.0 Default value expression
//
public class DefaultValueExpression : Expression
{
Expression expr;
public DefaultValueExpression (Expression expr, Location loc)
{
this.expr = expr;
this.loc = loc;
}
public Expression Expr {
get {
return this.expr;
}
}
public override bool IsSideEffectFree {
get {
return true;
}
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (2);
args.Add (new Argument (this));
args.Add (new Argument (new TypeOf (type, loc)));
return CreateExpressionFactoryCall (ec, "Constant", args);
}
protected override Expression DoResolve (ResolveContext ec)
{
type = expr.ResolveAsType (ec);
if (type == null)
return null;
if (type.IsStatic) {
ec.Report.Error (-244, loc, "The `default value' operator cannot be applied to an operand of a static type");
}
if (type.IsPointer)
return new NullLiteral (Location).ConvertImplicitly (type);
if (TypeSpec.IsReferenceType (type))
return new NullConstant (type, loc);
Constant c = New.Constantify (type, expr.Location);
if (c != null)
return c;
eclass = ExprClass.Variable;
return this;
}
public override void Emit (EmitContext ec)
{
LocalTemporary temp_storage = new LocalTemporary(type);
temp_storage.AddressOf(ec, AddressOp.LoadStore);
ec.Emit(OpCodes.Initobj, type);
temp_storage.Emit(ec);
temp_storage.Release (ec);
}
#if (NET_4_0 || MONODROID) && !STATIC
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
return SLE.Expression.Default (type.GetMetaInfo ());
}
#endif
protected override void CloneTo (CloneContext clonectx, Expression t)
{
DefaultValueExpression target = (DefaultValueExpression) t;
target.expr = expr.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Binary operators
/// </summary>
public class Binary : Expression, IDynamicBinder
{
public class PredefinedOperator
{
protected readonly TypeSpec left;
protected readonly TypeSpec right;
public readonly Operator OperatorsMask;
public TypeSpec ReturnType;
public PredefinedOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask)
: this (ltype, rtype, op_mask, ltype)
{
}
public PredefinedOperator (TypeSpec type, Operator op_mask, TypeSpec return_type)
: this (type, type, op_mask, return_type)
{
}
public PredefinedOperator (TypeSpec type, Operator op_mask)
: this (type, type, op_mask, type)
{
}
public PredefinedOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec return_type)
{
if ((op_mask & Operator.ValuesOnlyMask) != 0)
throw new InternalErrorException ("Only masked values can be used");
this.left = ltype;
this.right = rtype;
this.OperatorsMask = op_mask;
this.ReturnType = return_type;
}
public virtual Expression ConvertResult (ResolveContext ec, Binary b)
{
b.type = ReturnType;
b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
//
// A user operators does not support multiple user conversions, but decimal type
// is considered to be predefined type therefore we apply predefined operators rules
// and then look for decimal user-operator implementation
//
if (left.BuiltinType == BuiltinTypeSpec.Type.Decimal)
return b.ResolveUserOperator (ec, b.left, b.right);
var c = b.right as Constant;
if (c != null) {
if (c.IsDefaultValue && (b.oper == Operator.Addition || b.oper == Operator.Subtraction || (b.oper == Operator.BitwiseOr && !(b is Nullable.LiftedBinaryOperator))))
return ReducedExpression.Create (b.left, b).Resolve (ec);
if ((b.oper == Operator.Multiply || b.oper == Operator.Division) && c.IsOneInteger)
return ReducedExpression.Create (b.left, b).Resolve (ec);
return b;
}
c = b.left as Constant;
if (c != null) {
if (c.IsDefaultValue && (b.oper == Operator.Addition || (b.oper == Operator.BitwiseOr && !(b is Nullable.LiftedBinaryOperator))))
return ReducedExpression.Create (b.right, b).Resolve (ec);
if (b.oper == Operator.Multiply && c.IsOneInteger)
return ReducedExpression.Create (b.right, b).Resolve (ec);
return b;
}
return b;
}
public bool IsPrimitiveApplicable (TypeSpec ltype, TypeSpec rtype)
{
//
// We are dealing with primitive types only
//
return left == ltype && ltype == rtype;
}
public virtual bool IsApplicable (ResolveContext ec, Expression lexpr, Expression rexpr)
{
// Quick path
if (left == lexpr.Type && right == rexpr.Type)
return true;
return Convert.ImplicitConversionExists (ec, lexpr, left) &&
Convert.ImplicitConversionExists (ec, rexpr, right);
}
public PredefinedOperator ResolveBetterOperator (ResolveContext ec, PredefinedOperator best_operator)
{
int result = 0;
if (left != null && best_operator.left != null) {
result = OverloadResolver.BetterTypeConversion (ec, best_operator.left, left);
}
//
// When second argument is same as the first one, the result is same
//
if (right != null && (left != right || best_operator.left != best_operator.right)) {
result |= OverloadResolver.BetterTypeConversion (ec, best_operator.right, right);
}
if (result == 0 || result > 2)
return null;
return result == 1 ? best_operator : this;
}
}
sealed class PredefinedStringOperator : PredefinedOperator
{
public PredefinedStringOperator (TypeSpec type, Operator op_mask, TypeSpec retType)
: base (type, type, op_mask, retType)
{
}
public PredefinedStringOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec retType)
: base (ltype, rtype, op_mask, retType)
{
}
public override Expression ConvertResult (ResolveContext ec, Binary b)
{
//
// Use original expression for nullable arguments
//
Nullable.Unwrap unwrap = b.left as Nullable.Unwrap;
if (unwrap != null)
b.left = unwrap.Original;
unwrap = b.right as Nullable.Unwrap;
if (unwrap != null)
b.right = unwrap.Original;
b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
//
// Start a new concat expression using converted expression
//
return StringConcat.Create (ec, b.left, b.right, b.loc);
}
}
sealed class PredefinedShiftOperator : PredefinedOperator
{
public PredefinedShiftOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask)
: base (ltype, rtype, op_mask)
{
}
public override Expression ConvertResult (ResolveContext ec, Binary b)
{
b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
Expression expr_tree_expr = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
int right_mask = left.BuiltinType == BuiltinTypeSpec.Type.Int || left.BuiltinType == BuiltinTypeSpec.Type.UInt ? 0x1f : 0x3f;
//
// b = b.left >> b.right & (0x1f|0x3f)
//
b.right = new Binary (Operator.BitwiseAnd,
b.right, new IntConstant (ec.BuiltinTypes, right_mask, b.right.Location), b.loc).Resolve (ec);
//
// Expression tree representation does not use & mask
//
b.right = ReducedExpression.Create (b.right, expr_tree_expr).Resolve (ec);
b.type = ReturnType;
//
// Optimize shift by 0
//
var c = b.right as Constant;
if (c != null && c.IsDefaultValue)
return ReducedExpression.Create (b.left, b).Resolve (ec);
return b;
}
}
sealed class PredefinedEqualityOperator : PredefinedOperator
{
MethodSpec equal_method, inequal_method;
public PredefinedEqualityOperator (TypeSpec arg, TypeSpec retType)
: base (arg, arg, Operator.EqualityMask, retType)
{
}
public override Expression ConvertResult (ResolveContext ec, Binary b)
{
b.type = ReturnType;
b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
Arguments args = new Arguments (2);
args.Add (new Argument (b.left));
args.Add (new Argument (b.right));
MethodSpec method;
if (b.oper == Operator.Equality) {
if (equal_method == null) {
if (left.BuiltinType == BuiltinTypeSpec.Type.String)
equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (b.loc);
else if (left.BuiltinType == BuiltinTypeSpec.Type.Delegate)
equal_method = ec.Module.PredefinedMembers.DelegateEqual.Resolve (b.loc);
else
throw new NotImplementedException (left.GetSignatureForError ());
}
method = equal_method;
} else {
if (inequal_method == null) {
if (left.BuiltinType == BuiltinTypeSpec.Type.String)
inequal_method = ec.Module.PredefinedMembers.StringInequal.Resolve (b.loc);
else if (left.BuiltinType == BuiltinTypeSpec.Type.Delegate)
inequal_method = ec.Module.PredefinedMembers.DelegateInequal.Resolve (b.loc);
else
throw new NotImplementedException (left.GetSignatureForError ());
}
method = inequal_method;
}
return new UserOperatorCall (method, args, b.CreateExpressionTree, b.loc);
}
}
class PredefinedPointerOperator : PredefinedOperator
{
public PredefinedPointerOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask)
: base (ltype, rtype, op_mask)
{
}
public PredefinedPointerOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec retType)
: base (ltype, rtype, op_mask, retType)
{
}
public PredefinedPointerOperator (TypeSpec type, Operator op_mask, TypeSpec return_type)
: base (type, op_mask, return_type)
{
}
public override bool IsApplicable (ResolveContext ec, Expression lexpr, Expression rexpr)
{
if (left == null) {
if (!lexpr.Type.IsPointer)
return false;
} else {
if (!Convert.ImplicitConversionExists (ec, lexpr, left))
return false;
}
if (right == null) {
if (!rexpr.Type.IsPointer)
return false;
} else {
if (!Convert.ImplicitConversionExists (ec, rexpr, right))
return false;
}
return true;
}
public override Expression ConvertResult (ResolveContext ec, Binary b)
{
if (left != null) {
b.left = EmptyCast.Create (b.left, left);
} else if (right != null) {
b.right = EmptyCast.Create (b.right, right);
}
TypeSpec r_type = ReturnType;
Expression left_arg, right_arg;
if (r_type == null) {
if (left == null) {
left_arg = b.left;
right_arg = b.right;
r_type = b.left.Type;
} else {
left_arg = b.right;
right_arg = b.left;
r_type = b.right.Type;
}
} else {
left_arg = b.left;
right_arg = b.right;
}
return new PointerArithmetic (b.oper, left_arg, right_arg, r_type, b.loc).Resolve (ec);
}
}
[Flags]
public enum Operator {
Multiply = 0 | ArithmeticMask,
Division = 1 | ArithmeticMask,
Modulus = 2 | ArithmeticMask,
Addition = 3 | ArithmeticMask | AdditionMask,
Subtraction = 4 | ArithmeticMask | SubtractionMask,
LeftShift = 5 | ShiftMask,
RightShift = 6 | ShiftMask,
LessThan = 7 | ComparisonMask | RelationalMask,
GreaterThan = 8 | ComparisonMask | RelationalMask,
LessThanOrEqual = 9 | ComparisonMask | RelationalMask,
GreaterThanOrEqual = 10 | ComparisonMask | RelationalMask,
Equality = 11 | ComparisonMask | EqualityMask,
Inequality = 12 | ComparisonMask | EqualityMask,
BitwiseAnd = 13 | BitwiseMask,
ExclusiveOr = 14 | BitwiseMask,
BitwiseOr = 15 | BitwiseMask,
LogicalAnd = 16 | LogicalMask,
LogicalOr = 17 | LogicalMask,
//
// Operator masks
//
ValuesOnlyMask = ArithmeticMask - 1,
ArithmeticMask = 1 << 5,
ShiftMask = 1 << 6,
ComparisonMask = 1 << 7,
EqualityMask = 1 << 8,
BitwiseMask = 1 << 9,
LogicalMask = 1 << 10,
AdditionMask = 1 << 11,
SubtractionMask = 1 << 12,
RelationalMask = 1 << 13
}
protected enum State
{
None = 0,
Compound = 1 << 1,
LeftNullLifted = 1 << 2,
RightNullLifted = 1 << 3
}
readonly Operator oper;
protected Expression left, right;
protected State state;
Expression enum_conversion;
public Binary (Operator oper, Expression left, Expression right, bool isCompound, Location loc)
: this (oper, left, right, loc)
{
if (isCompound)
state |= State.Compound;
}
public Binary (Operator oper, Expression left, Expression right, Location loc)
{
this.oper = oper;
this.left = left;
this.right = right;
this.loc = loc;
}
#region Properties
public bool IsCompound {
get {
return (state & State.Compound) != 0;
}
}
public Operator Oper {
get {
return oper;
}
}
public Expression Left {
get {
return this.left;
}
}
public Expression Right {
get {
return this.right;
}
}
#endregion
/// <summary>
/// Returns a stringified representation of the Operator
/// </summary>
string OperName (Operator oper)
{
string s;
switch (oper){
case Operator.Multiply:
s = "*";
break;
case Operator.Division:
s = "/";
break;
case Operator.Modulus:
s = "%";
break;
case Operator.Addition:
s = "+";
break;
case Operator.Subtraction:
s = "-";
break;
case Operator.LeftShift:
s = "<<";
break;
case Operator.RightShift:
s = ">>";
break;
case Operator.LessThan:
s = "<";
break;
case Operator.GreaterThan:
s = ">";
break;
case Operator.LessThanOrEqual:
s = "<=";
break;
case Operator.GreaterThanOrEqual:
s = ">=";
break;
case Operator.Equality:
s = "==";
break;
case Operator.Inequality:
s = "!=";
break;
case Operator.BitwiseAnd:
s = "&";
break;
case Operator.BitwiseOr:
s = "|";
break;
case Operator.ExclusiveOr:
s = "^";
break;
case Operator.LogicalOr:
s = "||";
break;
case Operator.LogicalAnd:
s = "&&";
break;
default:
s = oper.ToString ();
break;
}
if (IsCompound)
return s + "=";
return s;
}
public static void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right, Operator oper, Location loc)
{
new Binary (oper, left, right, loc).Error_OperatorCannotBeApplied (ec, left, right);
}
public static void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right, string oper, Location loc)
{
if (left.Type == InternalType.ErrorType || right.Type == InternalType.ErrorType)
return;
string l, r;
l = TypeManager.CSharpName (left.Type);
r = TypeManager.CSharpName (right.Type);
ec.Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
oper, l, r);
}
protected void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right)
{
Error_OperatorCannotBeApplied (ec, left, right, OperName (oper), loc);
}
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
string GetOperatorExpressionTypeName ()
{
switch (oper) {
case Operator.Addition:
return IsCompound ? "AddAssign" : "Add";
case Operator.BitwiseAnd:
return IsCompound ? "AndAssign" : "And";
case Operator.BitwiseOr:
return IsCompound ? "OrAssign" : "Or";
case Operator.Division:
return IsCompound ? "DivideAssign" : "Divide";
case Operator.ExclusiveOr:
return IsCompound ? "ExclusiveOrAssign" : "ExclusiveOr";
case Operator.Equality:
return "Equal";
case Operator.GreaterThan:
return "GreaterThan";
case Operator.GreaterThanOrEqual:
return "GreaterThanOrEqual";
case Operator.Inequality:
return "NotEqual";
case Operator.LeftShift:
return IsCompound ? "LeftShiftAssign" : "LeftShift";
case Operator.LessThan:
return "LessThan";
case Operator.LessThanOrEqual:
return "LessThanOrEqual";
case Operator.LogicalAnd:
return "And";
case Operator.LogicalOr:
return "Or";
case Operator.Modulus:
return IsCompound ? "ModuloAssign" : "Modulo";
case Operator.Multiply:
return IsCompound ? "MultiplyAssign" : "Multiply";
case Operator.RightShift:
return IsCompound ? "RightShiftAssign" : "RightShift";
case Operator.Subtraction:
return IsCompound ? "SubtractAssign" : "Subtract";
default:
throw new NotImplementedException ("Unknown expression type operator " + oper.ToString ());
}
}
static CSharp.Operator.OpType ConvertBinaryToUserOperator (Operator op)
{
switch (op) {
case Operator.Addition:
return CSharp.Operator.OpType.Addition;
case Operator.BitwiseAnd:
case Operator.LogicalAnd:
return CSharp.Operator.OpType.BitwiseAnd;
case Operator.BitwiseOr:
case Operator.LogicalOr:
return CSharp.Operator.OpType.BitwiseOr;
case Operator.Division:
return CSharp.Operator.OpType.Division;
case Operator.Equality:
return CSharp.Operator.OpType.Equality;
case Operator.ExclusiveOr:
return CSharp.Operator.OpType.ExclusiveOr;
case Operator.GreaterThan:
return CSharp.Operator.OpType.GreaterThan;
case Operator.GreaterThanOrEqual:
return CSharp.Operator.OpType.GreaterThanOrEqual;
case Operator.Inequality:
return CSharp.Operator.OpType.Inequality;
case Operator.LeftShift:
return CSharp.Operator.OpType.LeftShift;
case Operator.LessThan:
return CSharp.Operator.OpType.LessThan;
case Operator.LessThanOrEqual:
return CSharp.Operator.OpType.LessThanOrEqual;
case Operator.Modulus:
return CSharp.Operator.OpType.Modulus;
case Operator.Multiply:
return CSharp.Operator.OpType.Multiply;
case Operator.RightShift:
return CSharp.Operator.OpType.RightShift;
case Operator.Subtraction:
return CSharp.Operator.OpType.Subtraction;
default:
throw new InternalErrorException (op.ToString ());
}
}
public override bool ContainsEmitWithAwait ()
{
return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
}
public static void EmitOperatorOpcode (EmitContext ec, Operator oper, TypeSpec l)
{
OpCode opcode;
switch (oper){
case Operator.Multiply:
if (ec.HasSet (EmitContext.Options.CheckedScope)) {
if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long)
opcode = OpCodes.Mul_Ovf;
else if (!IsFloat (l))
opcode = OpCodes.Mul_Ovf_Un;
else
opcode = OpCodes.Mul;
} else
opcode = OpCodes.Mul;
break;
case Operator.Division:
if (IsUnsigned (l))
opcode = OpCodes.Div_Un;
else
opcode = OpCodes.Div;
break;
case Operator.Modulus:
if (IsUnsigned (l))
opcode = OpCodes.Rem_Un;
else
opcode = OpCodes.Rem;
break;
case Operator.Addition:
if (ec.HasSet (EmitContext.Options.CheckedScope)) {
if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long)
opcode = OpCodes.Add_Ovf;
else if (!IsFloat (l))
opcode = OpCodes.Add_Ovf_Un;
else
opcode = OpCodes.Add;
} else
opcode = OpCodes.Add;
break;
case Operator.Subtraction:
if (ec.HasSet (EmitContext.Options.CheckedScope)) {
if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long)
opcode = OpCodes.Sub_Ovf;
else if (!IsFloat (l))
opcode = OpCodes.Sub_Ovf_Un;
else
opcode = OpCodes.Sub;
} else
opcode = OpCodes.Sub;
break;
case Operator.RightShift:
if (IsUnsigned (l))
opcode = OpCodes.Shr_Un;
else
opcode = OpCodes.Shr;
break;
case Operator.LeftShift:
opcode = OpCodes.Shl;
break;
case Operator.Equality:
opcode = OpCodes.Ceq;
break;
case Operator.Inequality:
ec.Emit (OpCodes.Ceq);
ec.EmitInt (0);
opcode = OpCodes.Ceq;
break;
case Operator.LessThan:
if (IsUnsigned (l))
opcode = OpCodes.Clt_Un;
else
opcode = OpCodes.Clt;
break;
case Operator.GreaterThan:
if (IsUnsigned (l))
opcode = OpCodes.Cgt_Un;
else
opcode = OpCodes.Cgt;
break;
case Operator.LessThanOrEqual:
if (IsUnsigned (l) || IsFloat (l))
ec.Emit (OpCodes.Cgt_Un);
else
ec.Emit (OpCodes.Cgt);
ec.EmitInt (0);
opcode = OpCodes.Ceq;
break;
case Operator.GreaterThanOrEqual:
if (IsUnsigned (l) || IsFloat (l))
ec.Emit (OpCodes.Clt_Un);
else
ec.Emit (OpCodes.Clt);
ec.EmitInt (0);
opcode = OpCodes.Ceq;
break;
case Operator.BitwiseOr:
opcode = OpCodes.Or;
break;
case Operator.BitwiseAnd:
opcode = OpCodes.And;
break;
case Operator.ExclusiveOr:
opcode = OpCodes.Xor;
break;
default:
throw new InternalErrorException (oper.ToString ());
}
ec.Emit (opcode);
}
static bool IsUnsigned (TypeSpec t)
{
switch (t.BuiltinType) {
case BuiltinTypeSpec.Type.Char:
case BuiltinTypeSpec.Type.UInt:
case BuiltinTypeSpec.Type.ULong:
case BuiltinTypeSpec.Type.UShort:
case BuiltinTypeSpec.Type.Byte:
return true;
}
return t.IsPointer;
}
static bool IsFloat (TypeSpec t)
{
return t.BuiltinType == BuiltinTypeSpec.Type.Float || t.BuiltinType == BuiltinTypeSpec.Type.Double;
}
Expression ResolveOperator (ResolveContext ec)
{
TypeSpec l = left.Type;
TypeSpec r = right.Type;
Expression expr;
bool primitives_only = false;
//
// Handles predefined primitive types
//
if (BuiltinTypeSpec.IsPrimitiveType (l) && BuiltinTypeSpec.IsPrimitiveType (r)) {
if ((oper & Operator.ShiftMask) == 0) {
if (l.BuiltinType != BuiltinTypeSpec.Type.Bool && !DoBinaryOperatorPromotion (ec))
return null;
primitives_only = true;
}
} else {
// Pointers
if (l.IsPointer || r.IsPointer)
return ResolveOperatorPointer (ec, l, r);
// Enums
bool lenum = l.IsEnum;
bool renum = r.IsEnum;
if (lenum || renum) {
expr = ResolveOperatorEnum (ec, lenum, renum, l, r);
if (expr != null)
return expr;
}
// Delegates
if ((oper == Operator.Addition || oper == Operator.Subtraction) && (l.IsDelegate || r.IsDelegate)) {
expr = ResolveOperatorDelegate (ec, l, r);
// TODO: Can this be ambiguous
if (expr != null)
return expr;
}
// User operators
expr = ResolveUserOperator (ec, left, right);
if (expr != null)
return expr;
// Predefined reference types equality
if ((oper & Operator.EqualityMask) != 0) {
expr = ResolveOperatorEquality (ec, l, r);
if (expr != null)
return expr;
}
}
return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryStandard, primitives_only, null);
}
// at least one of 'left' or 'right' is an enumeration constant (EnumConstant or SideEffectConstant or ...)
// if 'left' is not an enumeration constant, create one from the type of 'right'
Constant EnumLiftUp (ResolveContext ec, Constant left, Constant right, Location loc)
{
switch (oper) {
case Operator.BitwiseOr:
case Operator.BitwiseAnd:
case Operator.ExclusiveOr:
case Operator.Equality:
case Operator.Inequality:
case Operator.LessThan:
case Operator.LessThanOrEqual:
case Operator.GreaterThan:
case Operator.GreaterThanOrEqual:
if (left.Type.IsEnum)
return left;
if (left.IsZeroInteger)
return left.TryReduce (ec, right.Type, loc);
break;
case Operator.Addition:
case Operator.Subtraction:
return left;
case Operator.Multiply:
case Operator.Division:
case Operator.Modulus:
case Operator.LeftShift:
case Operator.RightShift:
if (right.Type.IsEnum || left.Type.IsEnum)
break;
return left;
}
return null;
}
//
// The `|' operator used on types which were extended is dangerous
//
void CheckBitwiseOrOnSignExtended (ResolveContext ec)
{
OpcodeCast lcast = left as OpcodeCast;
if (lcast != null) {
if (IsUnsigned (lcast.UnderlyingType))
lcast = null;
}
OpcodeCast rcast = right as OpcodeCast;
if (rcast != null) {
if (IsUnsigned (rcast.UnderlyingType))
rcast = null;
}
if (lcast == null && rcast == null)
return;
// FIXME: consider constants
ec.Report.Warning (675, 3, loc,
"The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
TypeManager.CSharpName (lcast != null ? lcast.UnderlyingType : rcast.UnderlyingType));
}
public static PredefinedOperator[] CreatePointerOperatorsTable (BuiltinTypes types)
{
return new PredefinedOperator[] {
//
// Pointer arithmetic:
//
// T* operator + (T* x, int y); T* operator - (T* x, int y);
// T* operator + (T* x, uint y); T* operator - (T* x, uint y);
// T* operator + (T* x, long y); T* operator - (T* x, long y);
// T* operator + (T* x, ulong y); T* operator - (T* x, ulong y);
//
new PredefinedPointerOperator (null, types.Int, Operator.AdditionMask | Operator.SubtractionMask),
new PredefinedPointerOperator (null, types.UInt, Operator.AdditionMask | Operator.SubtractionMask),
new PredefinedPointerOperator (null, types.Long, Operator.AdditionMask | Operator.SubtractionMask),
new PredefinedPointerOperator (null, types.ULong, Operator.AdditionMask | Operator.SubtractionMask),
//
// T* operator + (int y, T* x);
// T* operator + (uint y, T *x);
// T* operator + (long y, T *x);
// T* operator + (ulong y, T *x);
//
new PredefinedPointerOperator (types.Int, null, Operator.AdditionMask, null),
new PredefinedPointerOperator (types.UInt, null, Operator.AdditionMask, null),
new PredefinedPointerOperator (types.Long, null, Operator.AdditionMask, null),
new PredefinedPointerOperator (types.ULong, null, Operator.AdditionMask, null),
//
// long operator - (T* x, T *y)
//
new PredefinedPointerOperator (null, Operator.SubtractionMask, types.Long)
};
}
public static PredefinedOperator[] CreateStandardOperatorsTable (BuiltinTypes types)
{
TypeSpec bool_type = types.Bool;
return new PredefinedOperator[] {
new PredefinedOperator (types.Int, Operator.ArithmeticMask | Operator.BitwiseMask),
new PredefinedOperator (types.UInt, Operator.ArithmeticMask | Operator.BitwiseMask),
new PredefinedOperator (types.Long, Operator.ArithmeticMask | Operator.BitwiseMask),
new PredefinedOperator (types.ULong, Operator.ArithmeticMask | Operator.BitwiseMask),
new PredefinedOperator (types.Float, Operator.ArithmeticMask),
new PredefinedOperator (types.Double, Operator.ArithmeticMask),
new PredefinedOperator (types.Decimal, Operator.ArithmeticMask),
new PredefinedOperator (types.Int, Operator.ComparisonMask, bool_type),
new PredefinedOperator (types.UInt, Operator.ComparisonMask, bool_type),
new PredefinedOperator (types.Long, Operator.ComparisonMask, bool_type),
new PredefinedOperator (types.ULong, Operator.ComparisonMask, bool_type),
new PredefinedOperator (types.Float, Operator.ComparisonMask, bool_type),
new PredefinedOperator (types.Double, Operator.ComparisonMask, bool_type),
new PredefinedOperator (types.Decimal, Operator.ComparisonMask, bool_type),
new PredefinedStringOperator (types.String, Operator.AdditionMask, types.String),
new PredefinedStringOperator (types.String, types.Object, Operator.AdditionMask, types.String),
new PredefinedStringOperator (types.Object, types.String, Operator.AdditionMask, types.String),
new PredefinedOperator (bool_type, Operator.BitwiseMask | Operator.LogicalMask | Operator.EqualityMask, bool_type),
new PredefinedShiftOperator (types.Int, types.Int, Operator.ShiftMask),
new PredefinedShiftOperator (types.UInt, types.Int, Operator.ShiftMask),
new PredefinedShiftOperator (types.Long, types.Int, Operator.ShiftMask),
new PredefinedShiftOperator (types.ULong, types.Int, Operator.ShiftMask)
};
}
public static PredefinedOperator[] CreateEqualityOperatorsTable (BuiltinTypes types)
{
TypeSpec bool_type = types.Bool;
return new PredefinedOperator[] {
new PredefinedEqualityOperator (types.String, bool_type),
new PredefinedEqualityOperator (types.Delegate, bool_type),
new PredefinedOperator (bool_type, Operator.EqualityMask, bool_type)
};
}
//
// Rules used during binary numeric promotion
//
static bool DoNumericPromotion (ResolveContext rc, ref Expression prim_expr, ref Expression second_expr, TypeSpec type)
{
Expression temp;
Constant c = prim_expr as Constant;
if (c != null) {
temp = c.ConvertImplicitly (type);
if (temp != null) {
prim_expr = temp;
return true;
}
}
if (type.BuiltinType == BuiltinTypeSpec.Type.UInt) {
switch (prim_expr.Type.BuiltinType) {
case BuiltinTypeSpec.Type.Int:
case BuiltinTypeSpec.Type.Short:
case BuiltinTypeSpec.Type.SByte:
case BuiltinTypeSpec.Type.Long:
type = rc.BuiltinTypes.Long;
if (type != second_expr.Type) {
c = second_expr as Constant;
if (c != null)
temp = c.ConvertImplicitly (type);
else
temp = Convert.ImplicitNumericConversion (second_expr, type);
if (temp == null)
return false;
second_expr = temp;
}
break;
}
} else if (type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
//
// A compile-time error occurs if the other operand is of type sbyte, short, int, or long
//
switch (type.BuiltinType) {
case BuiltinTypeSpec.Type.Int:
case BuiltinTypeSpec.Type.Long:
case BuiltinTypeSpec.Type.Short:
case BuiltinTypeSpec.Type.SByte:
return false;
}
}
temp = Convert.ImplicitNumericConversion (prim_expr, type);
if (temp == null)
return false;
prim_expr = temp;
return true;
}
//
// 7.2.6.2 Binary numeric promotions
//
public bool DoBinaryOperatorPromotion (ResolveContext ec)
{
TypeSpec ltype = left.Type;
TypeSpec rtype = right.Type;
Expression temp;
foreach (TypeSpec t in ec.BuiltinTypes.BinaryPromotionsTypes) {
if (t == ltype)
return t == rtype || DoNumericPromotion (ec, ref right, ref left, t);
if (t == rtype)
return t == ltype || DoNumericPromotion (ec, ref left, ref right, t);
}
TypeSpec int32 = ec.BuiltinTypes.Int;
if (ltype != int32) {
Constant c = left as Constant;
if (c != null)
temp = c.ConvertImplicitly (int32);
else
temp = Convert.ImplicitNumericConversion (left, int32);
if (temp == null)
return false;
left = temp;
}
if (rtype != int32) {
Constant c = right as Constant;
if (c != null)
temp = c.ConvertImplicitly (int32);
else
temp = Convert.ImplicitNumericConversion (right, int32);
if (temp == null)
return false;
right = temp;
}
return true;
}
protected override Expression DoResolve (ResolveContext ec)
{
if (left == null)
return null;
if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
left = ((ParenthesizedExpression) left).Expr;
left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
if (left == null)
return null;
if (left.eclass == ExprClass.Type) {
ec.Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
return null;
}
} else
left = left.Resolve (ec);
if (left == null)
return null;
Constant lc = left as Constant;
if (lc != null && lc.Type.BuiltinType == BuiltinTypeSpec.Type.Bool &&
((oper == Operator.LogicalAnd && lc.IsDefaultValue) ||
(oper == Operator.LogicalOr && !lc.IsDefaultValue))) {
// FIXME: resolve right expression as unreachable
// right.Resolve (ec);
ec.Report.Warning (429, 4, loc, "Unreachable expression code detected");
return left;
}
right = right.Resolve (ec);
if (right == null)
return null;
eclass = ExprClass.Value;
Constant rc = right as Constant;
// The conversion rules are ignored in enum context but why
if (!ec.HasSet (ResolveContext.Options.EnumScope) && lc != null && rc != null && (left.Type.IsEnum || right.Type.IsEnum)) {
lc = EnumLiftUp (ec, lc, rc, loc);
if (lc != null)
rc = EnumLiftUp (ec, rc, lc, loc);
}
if (rc != null && lc != null) {
int prev_e = ec.Report.Errors;
Expression e = ConstantFold.BinaryFold (ec, oper, lc, rc, loc);
if (e != null || ec.Report.Errors != prev_e)
return e;
}
// Comparison warnings
if ((oper & Operator.ComparisonMask) != 0) {
if (left.Equals (right)) {
ec.Report.Warning (1718, 3, loc, "A comparison made to same variable. Did you mean to compare something else?");
}
CheckOutOfRangeComparison (ec, lc, right.Type);
CheckOutOfRangeComparison (ec, rc, left.Type);
}
if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic || right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
var lt = left.Type;
var rt = right.Type;
if (lt.Kind == MemberKind.Void || lt == InternalType.MethodGroup || lt == InternalType.AnonymousMethod ||
rt.Kind == MemberKind.Void || rt == InternalType.MethodGroup || rt == InternalType.AnonymousMethod) {
Error_OperatorCannotBeApplied (ec, left, right);
return null;
}
Arguments args;
//
// Special handling for logical boolean operators which require rhs not to be
// evaluated based on lhs value
//
if ((oper & Operator.LogicalMask) != 0) {
Expression cond_left, cond_right, expr;
args = new Arguments (2);
if (lt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
LocalVariable temp = LocalVariable.CreateCompilerGenerated (lt, ec.CurrentBlock, loc);
var cond_args = new Arguments (1);
cond_args.Add (new Argument (new SimpleAssign (temp.CreateReferenceExpression (ec, loc), left).Resolve (ec)));
//
// dynamic && bool => IsFalse (temp = left) ? temp : temp && right;
// dynamic || bool => IsTrue (temp = left) ? temp : temp || right;
//
left = temp.CreateReferenceExpression (ec, loc);
if (oper == Operator.LogicalAnd) {
expr = DynamicUnaryConversion.CreateIsFalse (ec, cond_args, loc);
cond_left = left;
} else {
expr = DynamicUnaryConversion.CreateIsTrue (ec, cond_args, loc);
cond_left = left;
}
args.Add (new Argument (left));
args.Add (new Argument (right));
cond_right = new DynamicExpressionStatement (this, args, loc);
} else {
LocalVariable temp = LocalVariable.CreateCompilerGenerated (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
args.Add (new Argument (temp.CreateReferenceExpression (ec, loc).Resolve (ec)));
args.Add (new Argument (right));
right = new DynamicExpressionStatement (this, args, loc);
//
// bool && dynamic => (temp = left) ? temp && right : temp;
// bool || dynamic => (temp = left) ? temp : temp || right;
//
if (oper == Operator.LogicalAnd) {
cond_left = right;
cond_right = temp.CreateReferenceExpression (ec, loc);
} else {
cond_left = temp.CreateReferenceExpression (ec, loc);
cond_right = right;
}
expr = new BooleanExpression (new SimpleAssign (temp.CreateReferenceExpression (ec, loc), left));
}
return new Conditional (expr, cond_left, cond_right, loc).Resolve (ec);
}
args = new Arguments (2);
args.Add (new Argument (left));
args.Add (new Argument (right));
return new DynamicExpressionStatement (this, args, loc).Resolve (ec);
}
if (ec.Module.Compiler.Settings.Version >= LanguageVersion.ISO_2 &&
((left.Type.IsNullableType && (right is NullLiteral || right.Type.IsNullableType || TypeSpec.IsValueType (right.Type))) ||
(TypeSpec.IsValueType (left.Type) && right is NullLiteral) ||
(right.Type.IsNullableType && (left is NullLiteral || left.Type.IsNullableType || TypeSpec.IsValueType (left.Type))) ||
(TypeSpec.IsValueType (right.Type) && left is NullLiteral))) {
var lifted = new Nullable.LiftedBinaryOperator (oper, left, right, loc);
lifted.state = state;
return lifted.Resolve (ec);
}
return DoResolveCore (ec, left, right);
}
protected Expression DoResolveCore (ResolveContext ec, Expression left_orig, Expression right_orig)
{
Expression expr = ResolveOperator (ec);
if (expr == null)
Error_OperatorCannotBeApplied (ec, left_orig, right_orig);
if (left == null || right == null)
throw new InternalErrorException ("Invalid conversion");
if (oper == Operator.BitwiseOr)
CheckBitwiseOrOnSignExtended (ec);
return expr;
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
var le = left.MakeExpression (ctx);
var re = right.MakeExpression (ctx);
bool is_checked = ctx.HasSet (BuilderContext.Options.CheckedScope);
switch (oper) {
case Operator.Addition:
return is_checked ? SLE.Expression.AddChecked (le, re) : SLE.Expression.Add (le, re);
case Operator.BitwiseAnd:
return SLE.Expression.And (le, re);
case Operator.BitwiseOr:
return SLE.Expression.Or (le, re);
case Operator.Division:
return SLE.Expression.Divide (le, re);
case Operator.Equality:
return SLE.Expression.Equal (le, re);
case Operator.ExclusiveOr:
return SLE.Expression.ExclusiveOr (le, re);
case Operator.GreaterThan:
return SLE.Expression.GreaterThan (le, re);
case Operator.GreaterThanOrEqual:
return SLE.Expression.GreaterThanOrEqual (le, re);
case Operator.Inequality:
return SLE.Expression.NotEqual (le, re);
case Operator.LeftShift:
return SLE.Expression.LeftShift (le, re);
case Operator.LessThan:
return SLE.Expression.LessThan (le, re);
case Operator.LessThanOrEqual:
return SLE.Expression.LessThanOrEqual (le, re);
case Operator.LogicalAnd:
return SLE.Expression.AndAlso (le, re);
case Operator.LogicalOr:
return SLE.Expression.OrElse (le, re);
case Operator.Modulus:
return SLE.Expression.Modulo (le, re);
case Operator.Multiply:
return is_checked ? SLE.Expression.MultiplyChecked (le, re) : SLE.Expression.Multiply (le, re);
case Operator.RightShift:
return SLE.Expression.RightShift (le, re);
case Operator.Subtraction:
return is_checked ? SLE.Expression.SubtractChecked (le, re) : SLE.Expression.Subtract (le, re);
default:
throw new NotImplementedException (oper.ToString ());
}
}
//
// D operator + (D x, D y)
// D operator - (D x, D y)
//
Expression ResolveOperatorDelegate (ResolveContext ec, TypeSpec l, TypeSpec r)
{
if (l != r && !TypeSpecComparer.Variant.IsEqual (r, l)) {
Expression tmp;
if (right.eclass == ExprClass.MethodGroup || r == InternalType.AnonymousMethod || r == InternalType.NullLiteral) {
tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
if (tmp == null)
return null;
right = tmp;
r = right.Type;
} else if (left.eclass == ExprClass.MethodGroup || (l == InternalType.AnonymousMethod || l == InternalType.NullLiteral)) {
tmp = Convert.ImplicitConversionRequired (ec, left, r, loc);
if (tmp == null)
return null;
left = tmp;
l = left.Type;
} else {
return null;
}
}
MethodSpec method = null;
Arguments args = new Arguments (2);
args.Add (new Argument (left));
args.Add (new Argument (right));
if (oper == Operator.Addition) {
method = ec.Module.PredefinedMembers.DelegateCombine.Resolve (loc);
} else if (oper == Operator.Subtraction) {
method = ec.Module.PredefinedMembers.DelegateRemove.Resolve (loc);
}
if (method == null)
return new EmptyExpression (ec.BuiltinTypes.Decimal);
MethodGroupExpr mg = MethodGroupExpr.CreatePredefined (method, ec.BuiltinTypes.Delegate, loc);
Expression expr = new UserOperatorCall (mg.BestCandidate, args, CreateExpressionTree, loc);
return new ClassCast (expr, l);
}
//
// Enumeration operators
//
Expression ResolveOperatorEnum (ResolveContext ec, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype)
{
//
// bool operator == (E x, E y);
// bool operator != (E x, E y);
// bool operator < (E x, E y);
// bool operator > (E x, E y);
// bool operator <= (E x, E y);
// bool operator >= (E x, E y);
//
// E operator & (E x, E y);
// E operator | (E x, E y);
// E operator ^ (E x, E y);
//
// U operator - (E e, E f)
// E operator - (E e, U x)
// E operator - (U x, E e) // LAMESPEC: Not covered by the specification
//
// E operator + (E e, U x)
// E operator + (U x, E e)
//
Expression ltemp = left;
Expression rtemp = right;
TypeSpec underlying_type;
TypeSpec underlying_type_result;
TypeSpec res_type;
Expression expr;
//
// LAMESPEC: There is never ambiguous conversion between enum operators
// the one which contains more enum parameters always wins even if there
// is an implicit conversion involved
//
if ((oper & (Operator.ComparisonMask | Operator.BitwiseMask)) != 0) {
if (renum) {
underlying_type = EnumSpec.GetUnderlyingType (rtype);
expr = Convert.ImplicitConversion (ec, left, rtype, loc);
if (expr == null)
return null;
left = expr;
ltype = expr.Type;
} else if (lenum) {
underlying_type = EnumSpec.GetUnderlyingType (ltype);
expr = Convert.ImplicitConversion (ec, right, ltype, loc);
if (expr == null)
return null;
right = expr;
rtype = expr.Type;
} else {
return null;
}
if ((oper & Operator.BitwiseMask) != 0) {
res_type = ltype;
underlying_type_result = underlying_type;
} else {
res_type = null;
underlying_type_result = null;
}
} else if (oper == Operator.Subtraction) {
if (renum) {
underlying_type = EnumSpec.GetUnderlyingType (rtype);
if (ltype != rtype) {
expr = Convert.ImplicitConversion (ec, left, rtype, left.Location);
if (expr == null) {
expr = Convert.ImplicitConversion (ec, left, underlying_type, left.Location);
if (expr == null)
return null;
res_type = rtype;
} else {
res_type = underlying_type;
}
left = expr;
} else {
res_type = underlying_type;
}
underlying_type_result = underlying_type;
} else if (lenum) {
underlying_type = EnumSpec.GetUnderlyingType (ltype);
expr = Convert.ImplicitConversion (ec, right, ltype, right.Location);
if (expr == null || expr is EnumConstant) {
expr = Convert.ImplicitConversion (ec, right, underlying_type, right.Location);
if (expr == null)
return null;
res_type = ltype;
} else {
res_type = underlying_type;
}
right = expr;
underlying_type_result = underlying_type;
} else {
return null;
}
} else if (oper == Operator.Addition) {
if (lenum) {
underlying_type = EnumSpec.GetUnderlyingType (ltype);
res_type = ltype;
if (rtype != underlying_type && (state & (State.RightNullLifted | State.LeftNullLifted)) == 0) {
expr = Convert.ImplicitConversion (ec, right, underlying_type, right.Location);
if (expr == null)
return null;
right = expr;
}
} else {
underlying_type = EnumSpec.GetUnderlyingType (rtype);
res_type = rtype;
if (ltype != underlying_type) {
expr = Convert.ImplicitConversion (ec, left, underlying_type, left.Location);
if (expr == null)
return null;
left = expr;
}
}
underlying_type_result = underlying_type;
} else {
return null;
}
// Unwrap the constant correctly, so DoBinaryOperatorPromotion can do the magic
// with constants and expressions
if (left.Type != underlying_type) {
if (left is Constant)
left = ((Constant) left).ConvertExplicitly (false, underlying_type);
else
left = EmptyCast.Create (left, underlying_type);
}
if (right.Type != underlying_type) {
if (right is Constant)
right = ((Constant) right).ConvertExplicitly (false, underlying_type);
else
right = EmptyCast.Create (right, underlying_type);
}
//
// C# specification uses explicit cast syntax which means binary promotion
// should happen, however it seems that csc does not do that
//
if (!DoBinaryOperatorPromotion (ec)) {
left = ltemp;
right = rtemp;
return null;
}
if (underlying_type_result != null && left.Type != underlying_type_result) {
enum_conversion = Convert.ExplicitNumericConversion (ec, new EmptyExpression (left.Type), underlying_type_result);
}
expr = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryStandard, true, res_type);
if (expr == null)
return null;
if (!IsCompound)
return expr;
//
// Section: 7.16.2
//
//
// If the return type of the selected operator is implicitly convertible to the type of x
//
if (Convert.ImplicitConversionExists (ec, expr, ltype))
return expr;
//
// Otherwise, if the selected operator is a predefined operator, if the return type of the
// selected operator is explicitly convertible to the type of x, and if y is implicitly
// convertible to the type of x or the operator is a shift operator, then the operation
// is evaluated as x = (T)(x op y), where T is the type of x
//
expr = Convert.ExplicitConversion (ec, expr, ltype, loc);
if (expr == null)
return null;
if (Convert.ImplicitConversionExists (ec, ltemp, ltype))
return expr;
return null;
}
//
// 7.9.6 Reference type equality operators
//
Expression ResolveOperatorEquality (ResolveContext ec, TypeSpec l, TypeSpec r)
{
Expression result;
type = ec.BuiltinTypes.Bool;
//
// a, Both operands are reference-type values or the value null
// b, One operand is a value of type T where T is a type-parameter and
// the other operand is the value null. Furthermore T does not have the
// value type constraint
//
// LAMESPEC: Very confusing details in the specification, basically any
// reference like type-parameter is allowed
//
var tparam_l = l as TypeParameterSpec;
var tparam_r = r as TypeParameterSpec;
if (tparam_l != null) {
if (right is NullLiteral && !tparam_l.HasSpecialStruct) {
left = new BoxedCast (left, ec.BuiltinTypes.Object);
return this;
}
if (!tparam_l.IsReferenceType)
return null;
l = tparam_l.GetEffectiveBase ();
left = new BoxedCast (left, l);
} else if (left is NullLiteral && tparam_r == null) {
if (!TypeSpec.IsReferenceType (r) || r.Kind == MemberKind.InternalCompilerType)
return null;
return this;
}
if (tparam_r != null) {
if (left is NullLiteral && !tparam_r.HasSpecialStruct) {
right = new BoxedCast (right, ec.BuiltinTypes.Object);
return this;
}
if (!tparam_r.IsReferenceType)
return null;
r = tparam_r.GetEffectiveBase ();
right = new BoxedCast (right, r);
} else if (right is NullLiteral) {
if (!TypeSpec.IsReferenceType (l) || l.Kind == MemberKind.InternalCompilerType)
return null;
return this;
}
bool no_arg_conv = false;
//
// LAMESPEC: method groups can be compared when they convert to other side delegate
//
if (l.IsDelegate) {
if (right.eclass == ExprClass.MethodGroup) {
result = Convert.ImplicitConversion (ec, right, l, loc);
if (result == null)
return null;
right = result;
r = l;
} else if (r.IsDelegate && l != r) {
return null;
}
} else if (left.eclass == ExprClass.MethodGroup && r.IsDelegate) {
result = Convert.ImplicitConversionRequired (ec, left, r, loc);
if (result == null)
return null;
left = result;
l = r;
} else {
no_arg_conv = l == r && !l.IsStruct;
}
//
// bool operator != (string a, string b)
// bool operator == (string a, string b)
//
// bool operator != (Delegate a, Delegate b)
// bool operator == (Delegate a, Delegate b)
//
// bool operator != (bool a, bool b)
// bool operator == (bool a, bool b)
//
// LAMESPEC: Reference equality comparison can apply to value/reference types when
// they implement an implicit conversion to any of types above. This does
// not apply when both operands are of same reference type
//
if (r.BuiltinType != BuiltinTypeSpec.Type.Object && l.BuiltinType != BuiltinTypeSpec.Type.Object) {
result = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryEquality, no_arg_conv, null);
if (result != null)
return result;
}
//
// bool operator != (object a, object b)
// bool operator == (object a, object b)
//
// An explicit reference conversion exists from the
// type of either operand to the type of the other operand.
//
// Optimize common path
if (l == r) {
return l.Kind == MemberKind.InternalCompilerType || l.Kind == MemberKind.Struct ? null : this;
}
if (!Convert.ExplicitReferenceConversionExists (l, r) &&
!Convert.ExplicitReferenceConversionExists (r, l))
return null;
// Reject allowed explicit conversions like int->object
if (!TypeSpec.IsReferenceType (l) || !TypeSpec.IsReferenceType (r))
return null;
if (l.BuiltinType == BuiltinTypeSpec.Type.String || l.BuiltinType == BuiltinTypeSpec.Type.Delegate || MemberCache.GetUserOperator (l, CSharp.Operator.OpType.Equality, false) != null)
ec.Report.Warning (253, 2, loc,
"Possible unintended reference comparison. Consider casting the right side expression to type `{0}' to get value comparison",
l.GetSignatureForError ());
if (r.BuiltinType == BuiltinTypeSpec.Type.String || r.BuiltinType == BuiltinTypeSpec.Type.Delegate || MemberCache.GetUserOperator (r, CSharp.Operator.OpType.Equality, false) != null)
ec.Report.Warning (252, 2, loc,
"Possible unintended reference comparison. Consider casting the left side expression to type `{0}' to get value comparison",
r.GetSignatureForError ());
return this;
}
Expression ResolveOperatorPointer (ResolveContext ec, TypeSpec l, TypeSpec r)
{
//
// bool operator == (void* x, void* y);
// bool operator != (void* x, void* y);
// bool operator < (void* x, void* y);
// bool operator > (void* x, void* y);
// bool operator <= (void* x, void* y);
// bool operator >= (void* x, void* y);
//
if ((oper & Operator.ComparisonMask) != 0) {
Expression temp;
if (!l.IsPointer) {
temp = Convert.ImplicitConversion (ec, left, r, left.Location);
if (temp == null)
return null;
left = temp;
}
if (!r.IsPointer) {
temp = Convert.ImplicitConversion (ec, right, l, right.Location);
if (temp == null)
return null;
right = temp;
}
type = ec.BuiltinTypes.Bool;
return this;
}
return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryUnsafe, false, null);
}
//
// Build-in operators method overloading
//
protected virtual Expression ResolveOperatorPredefined (ResolveContext ec, PredefinedOperator [] operators, bool primitives_only, TypeSpec enum_type)
{
PredefinedOperator best_operator = null;
TypeSpec l = left.Type;
TypeSpec r = right.Type;
Operator oper_mask = oper & ~Operator.ValuesOnlyMask;
foreach (PredefinedOperator po in operators) {
if ((po.OperatorsMask & oper_mask) == 0)
continue;
if (primitives_only) {
if (!po.IsPrimitiveApplicable (l, r))
continue;
} else {
if (!po.IsApplicable (ec, left, right))
continue;
}
if (best_operator == null) {
best_operator = po;
if (primitives_only)
break;
continue;
}
best_operator = po.ResolveBetterOperator (ec, best_operator);
if (best_operator == null) {
ec.Report.Error (34, loc, "Operator `{0}' is ambiguous on operands of type `{1}' and `{2}'",
OperName (oper), TypeManager.CSharpName (l), TypeManager.CSharpName (r));
best_operator = po;
break;
}
}
if (best_operator == null)
return null;
Expression expr = best_operator.ConvertResult (ec, this);
//
// Optimize &/&& constant expressions with 0 value
//
if (oper == Operator.BitwiseAnd || oper == Operator.LogicalAnd) {
Constant rc = right as Constant;
Constant lc = left as Constant;
if (((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue)) && !(this is Nullable.LiftedBinaryOperator)) {
//
// The result is a constant with side-effect
//
Constant side_effect = rc == null ?
new SideEffectConstant (lc, right, loc) :
new SideEffectConstant (rc, left, loc);
return ReducedExpression.Create (side_effect, expr);
}
}
if (enum_type == null)
return expr;
//
// HACK: required by enum_conversion
//
expr.Type = enum_type;
return EmptyCast.Create (expr, enum_type);
}
//
// Performs user-operator overloading
//
protected virtual Expression ResolveUserOperator (ResolveContext ec, Expression left, Expression right)
{
var op = ConvertBinaryToUserOperator (oper);
var l = left.Type;
if (l.IsNullableType)
l = Nullable.NullableInfo.GetUnderlyingType (l);
var r = right.Type;
if (r.IsNullableType)
r = Nullable.NullableInfo.GetUnderlyingType (r);
IList<MemberSpec> left_operators = MemberCache.GetUserOperator (l, op, false);
IList<MemberSpec> right_operators = null;
if (l != r) {
right_operators = MemberCache.GetUserOperator (r, op, false);
if (right_operators == null && left_operators == null)
return null;
} else if (left_operators == null) {
return null;
}
Arguments args = new Arguments (2);
Argument larg = new Argument (left);
args.Add (larg);
Argument rarg = new Argument (right);
args.Add (rarg);
//
// User-defined operator implementations always take precedence
// over predefined operator implementations
//
if (left_operators != null && right_operators != null) {
left_operators = CombineUserOperators (left_operators, right_operators);
} else if (right_operators != null) {
left_operators = right_operators;
}
var res = new OverloadResolver (left_operators, OverloadResolver.Restrictions.ProbingOnly |
OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded, loc);
var oper_method = res.ResolveOperator (ec, ref args);
if (oper_method == null)
return null;
var llifted = (state & State.LeftNullLifted) != 0;
var rlifted = (state & State.RightNullLifted) != 0;
if ((Oper & Operator.EqualityMask) != 0) {
var parameters = oper_method.Parameters;
// LAMESPEC: No idea why this is not allowed
if ((left is Nullable.Unwrap || right is Nullable.Unwrap) && parameters.Types [0] != parameters.Types [1])
return null;
// Binary operation was lifted but we have found a user operator
// which requires value-type argument, we downgrade ourself back to
// binary operation
// LAMESPEC: The user operator is not called (it cannot be we are passing null to struct)
// but compilation succeeds
if ((llifted && !parameters.Types[0].IsStruct) || (rlifted && !parameters.Types[1].IsStruct)) {
state &= ~(State.LeftNullLifted | State.RightNullLifted);
}
}
Expression oper_expr;
// TODO: CreateExpressionTree is allocated every time
if ((oper & Operator.LogicalMask) != 0) {
oper_expr = new ConditionalLogicalOperator (oper_method, args, CreateExpressionTree,
oper == Operator.LogicalAnd, loc).Resolve (ec);
} else {
oper_expr = new UserOperatorCall (oper_method, args, CreateExpressionTree, loc);
}
if (!llifted)
this.left = larg.Expr;
if (!rlifted)
this.right = rarg.Expr;
return oper_expr;
}
//
// Merge two sets of user operators into one, they are mostly distinguish
// except when they share base type and it contains an operator
//
static IList<MemberSpec> CombineUserOperators (IList<MemberSpec> left, IList<MemberSpec> right)
{
var combined = new List<MemberSpec> (left.Count + right.Count);
combined.AddRange (left);
foreach (var r in right) {
bool same = false;
foreach (var l in left) {
if (l.DeclaringType == r.DeclaringType) {
same = true;
break;
}
}
if (!same)
combined.Add (r);
}
return combined;
}
void CheckOutOfRangeComparison (ResolveContext ec, Constant c, TypeSpec type)
{
if (c is IntegralConstant || c is CharConstant) {
try {
c.ConvertExplicitly (true, type);
} catch (OverflowException) {
ec.Report.Warning (652, 2, loc,
"A comparison between a constant and a variable is useless. The constant is out of the range of the variable type `{0}'",
TypeManager.CSharpName (type));
}
}
}
/// <remarks>
/// EmitBranchable is called from Statement.EmitBoolExpression in the
/// context of a conditional bool expression. This function will return
/// false if it is was possible to use EmitBranchable, or true if it was.
///
/// The expression's code is generated, and we will generate a branch to `target'
/// if the resulting expression value is equal to isTrue
/// </remarks>
public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
{
//
// This is more complicated than it looks, but its just to avoid
// duplicated tests: basically, we allow ==, !=, >, <, >= and <=
// but on top of that we want for == and != to use a special path
// if we are comparing against null
//
if ((oper & Operator.EqualityMask) != 0 && (left is Constant || right is Constant)) {
bool my_on_true = oper == Operator.Inequality ? on_true : !on_true;
//
// put the constant on the rhs, for simplicity
//
if (left is Constant) {
Expression swap = right;
right = left;
left = swap;
}
//
// brtrue/brfalse works with native int only
//
if (((Constant) right).IsZeroInteger && right.Type.BuiltinType != BuiltinTypeSpec.Type.Long && right.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
left.EmitBranchable (ec, target, my_on_true);
return;
}
if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) {
// right is a boolean, and it's not 'false' => it is 'true'
left.EmitBranchable (ec, target, !my_on_true);
return;
}
} else if (oper == Operator.LogicalAnd) {
if (on_true) {
Label tests_end = ec.DefineLabel ();
left.EmitBranchable (ec, tests_end, false);
right.EmitBranchable (ec, target, true);
ec.MarkLabel (tests_end);
} else {
//
// This optimizes code like this
// if (true && i > 4)
//
if (!(left is Constant))
left.EmitBranchable (ec, target, false);
if (!(right is Constant))
right.EmitBranchable (ec, target, false);
}
return;
} else if (oper == Operator.LogicalOr){
if (on_true) {
left.EmitBranchable (ec, target, true);
right.EmitBranchable (ec, target, true);
} else {
Label tests_end = ec.DefineLabel ();
left.EmitBranchable (ec, tests_end, true);
right.EmitBranchable (ec, target, false);
ec.MarkLabel (tests_end);
}
return;
} else if ((oper & Operator.ComparisonMask) == 0) {
base.EmitBranchable (ec, target, on_true);
return;
}
left.Emit (ec);
right.Emit (ec);
TypeSpec t = left.Type;
bool is_float = IsFloat (t);
bool is_unsigned = is_float || IsUnsigned (t);
switch (oper){
case Operator.Equality:
if (on_true)
ec.Emit (OpCodes.Beq, target);
else
ec.Emit (OpCodes.Bne_Un, target);
break;
case Operator.Inequality:
if (on_true)
ec.Emit (OpCodes.Bne_Un, target);
else
ec.Emit (OpCodes.Beq, target);
break;
case Operator.LessThan:
if (on_true)
if (is_unsigned && !is_float)
ec.Emit (OpCodes.Blt_Un, target);
else
ec.Emit (OpCodes.Blt, target);
else
if (is_unsigned)
ec.Emit (OpCodes.Bge_Un, target);
else
ec.Emit (OpCodes.Bge, target);
break;
case Operator.GreaterThan:
if (on_true)
if (is_unsigned && !is_float)
ec.Emit (OpCodes.Bgt_Un, target);
else
ec.Emit (OpCodes.Bgt, target);
else
if (is_unsigned)
ec.Emit (OpCodes.Ble_Un, target);
else
ec.Emit (OpCodes.Ble, target);
break;
case Operator.LessThanOrEqual:
if (on_true)
if (is_unsigned && !is_float)
ec.Emit (OpCodes.Ble_Un, target);
else
ec.Emit (OpCodes.Ble, target);
else
if (is_unsigned)
ec.Emit (OpCodes.Bgt_Un, target);
else
ec.Emit (OpCodes.Bgt, target);
break;
case Operator.GreaterThanOrEqual:
if (on_true)
if (is_unsigned && !is_float)
ec.Emit (OpCodes.Bge_Un, target);
else
ec.Emit (OpCodes.Bge, target);
else
if (is_unsigned)
ec.Emit (OpCodes.Blt_Un, target);
else
ec.Emit (OpCodes.Blt, target);
break;
default:
throw new InternalErrorException (oper.ToString ());
}
}
public override void Emit (EmitContext ec)
{
EmitOperator (ec, left.Type);
}
protected virtual void EmitOperator (EmitContext ec, TypeSpec l)
{
if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) {
left = left.EmitToField (ec);
if ((oper & Operator.LogicalMask) == 0) {
right = right.EmitToField (ec);
}
}
//
// Handle short-circuit operators differently
// than the rest
//
if ((oper & Operator.LogicalMask) != 0) {
Label load_result = ec.DefineLabel ();
Label end = ec.DefineLabel ();
bool is_or = oper == Operator.LogicalOr;
left.EmitBranchable (ec, load_result, is_or);
right.Emit (ec);
ec.Emit (OpCodes.Br_S, end);
ec.MarkLabel (load_result);
ec.EmitInt (is_or ? 1 : 0);
ec.MarkLabel (end);
return;
}
//
// Optimize zero-based operations which cannot be optimized at expression level
//
if (oper == Operator.Subtraction) {
var lc = left as IntegralConstant;
if (lc != null && lc.IsDefaultValue) {
right.Emit (ec);
ec.Emit (OpCodes.Neg);
return;
}
}
left.Emit (ec);
right.Emit (ec);
EmitOperatorOpcode (ec, oper, l);
//
// Nullable enum could require underlying type cast and we cannot simply wrap binary
// expression because that would wrap lifted binary operation
//
if (enum_conversion != null)
enum_conversion.Emit (ec);
}
public override void EmitSideEffect (EmitContext ec)
{
if ((oper & Operator.LogicalMask) != 0 ||
(ec.HasSet (EmitContext.Options.CheckedScope) && (oper == Operator.Multiply || oper == Operator.Addition || oper == Operator.Subtraction))) {
base.EmitSideEffect (ec);
} else {
left.EmitSideEffect (ec);
right.EmitSideEffect (ec);
}
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Binary target = (Binary) t;
target.left = left.Clone (clonectx);
target.right = right.Clone (clonectx);
}
public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
{
Arguments binder_args = new Arguments (4);
MemberAccess sle = new MemberAccess (new MemberAccess (
new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc);
CSharpBinderFlags flags = 0;
if (ec.HasSet (ResolveContext.Options.CheckedScope))
flags = CSharpBinderFlags.CheckedContext;
if ((oper & Operator.LogicalMask) != 0)
flags |= CSharpBinderFlags.BinaryOperationLogical;
binder_args.Add (new Argument (new EnumConstant (new IntLiteral (ec.BuiltinTypes, (int) flags, loc), ec.Module.PredefinedTypes.BinderFlags.Resolve ())));
binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), GetOperatorExpressionTypeName (), loc)));
binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc)));
binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
return new Invocation (new MemberAccess (new TypeExpression (ec.Module.PredefinedTypes.Binder.TypeSpec, loc), "BinaryOperation", loc), binder_args);
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
return CreateExpressionTree (ec, null);
}
Expression CreateExpressionTree (ResolveContext ec, Expression method)
{
string method_name;
bool lift_arg = false;
switch (oper) {
case Operator.Addition:
if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type))
method_name = "AddChecked";
else
method_name = "Add";
break;
case Operator.BitwiseAnd:
method_name = "And";
break;
case Operator.BitwiseOr:
method_name = "Or";
break;
case Operator.Division:
method_name = "Divide";
break;
case Operator.Equality:
method_name = "Equal";
lift_arg = true;
break;
case Operator.ExclusiveOr:
method_name = "ExclusiveOr";
break;
case Operator.GreaterThan:
method_name = "GreaterThan";
lift_arg = true;
break;
case Operator.GreaterThanOrEqual:
method_name = "GreaterThanOrEqual";
lift_arg = true;
break;
case Operator.Inequality:
method_name = "NotEqual";
lift_arg = true;
break;
case Operator.LeftShift:
method_name = "LeftShift";
break;
case Operator.LessThan:
method_name = "LessThan";
lift_arg = true;
break;
case Operator.LessThanOrEqual:
method_name = "LessThanOrEqual";
lift_arg = true;
break;
case Operator.LogicalAnd:
method_name = "AndAlso";
break;
case Operator.LogicalOr:
method_name = "OrElse";
break;
case Operator.Modulus:
method_name = "Modulo";
break;
case Operator.Multiply:
if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type))
method_name = "MultiplyChecked";
else
method_name = "Multiply";
break;
case Operator.RightShift:
method_name = "RightShift";
break;
case Operator.Subtraction:
if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type))
method_name = "SubtractChecked";
else
method_name = "Subtract";
break;
default:
throw new InternalErrorException ("Unknown expression tree binary operator " + oper);
}
Arguments args = new Arguments (2);
args.Add (new Argument (left.CreateExpressionTree (ec)));
args.Add (new Argument (right.CreateExpressionTree (ec)));
if (method != null) {
if (lift_arg)
args.Add (new Argument (new BoolLiteral (ec.BuiltinTypes, false, loc)));
args.Add (new Argument (method));
}
return CreateExpressionFactoryCall (ec, method_name, args);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
//
// Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
// b, c, d... may be strings or objects.
//
public class StringConcat : Expression
{
Arguments arguments;
StringConcat (Location loc)
{
this.loc = loc;
arguments = new Arguments (2);
}
public override bool ContainsEmitWithAwait ()
{
return arguments.ContainsEmitWithAwait ();
}
public static StringConcat Create (ResolveContext rc, Expression left, Expression right, Location loc)
{
if (left.eclass == ExprClass.Unresolved || right.eclass == ExprClass.Unresolved)
throw new ArgumentException ();
var s = new StringConcat (loc);
s.type = rc.BuiltinTypes.String;
s.eclass = ExprClass.Value;
s.Append (rc, left);
s.Append (rc, right);
return s;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Argument arg = arguments [0];
return CreateExpressionAddCall (ec, arg, arg.CreateExpressionTree (ec), 1);
}
//
// Creates nested calls tree from an array of arguments used for IL emit
//
Expression CreateExpressionAddCall (ResolveContext ec, Argument left, Expression left_etree, int pos)
{
Arguments concat_args = new Arguments (2);
Arguments add_args = new Arguments (3);
concat_args.Add (left);
add_args.Add (new Argument (left_etree));
concat_args.Add (arguments [pos]);
add_args.Add (new Argument (arguments [pos].CreateExpressionTree (ec)));
var methods = GetConcatMethodCandidates ();
if (methods == null)
return null;
var res = new OverloadResolver (methods, OverloadResolver.Restrictions.NoBaseMembers, loc);
var method = res.ResolveMember<MethodSpec> (ec, ref concat_args);
if (method == null)
return null;
add_args.Add (new Argument (new TypeOfMethod (method, loc)));
Expression expr = CreateExpressionFactoryCall (ec, "Add", add_args);
if (++pos == arguments.Count)
return expr;
left = new Argument (new EmptyExpression (method.ReturnType));
return CreateExpressionAddCall (ec, left, expr, pos);
}
protected override Expression DoResolve (ResolveContext ec)
{
return this;
}
void Append (ResolveContext rc, Expression operand)
{
//
// Constant folding
//
StringConstant sc = operand as StringConstant;
if (sc != null) {
if (arguments.Count != 0) {
Argument last_argument = arguments [arguments.Count - 1];
StringConstant last_expr_constant = last_argument.Expr as StringConstant;
if (last_expr_constant != null) {
last_argument.Expr = new StringConstant (rc.BuiltinTypes, last_expr_constant.Value + sc.Value, sc.Location);
return;
}
}
} else {
//
// Multiple (3+) concatenation are resolved as multiple StringConcat instances
//
StringConcat concat_oper = operand as StringConcat;
if (concat_oper != null) {
arguments.AddRange (concat_oper.arguments);
return;
}
}
arguments.Add (new Argument (operand));
}
IList<MemberSpec> GetConcatMethodCandidates ()
{
return MemberCache.FindMembers (type, "Concat", true);
}
public override void Emit (EmitContext ec)
{
var members = GetConcatMethodCandidates ();
var res = new OverloadResolver (members, OverloadResolver.Restrictions.NoBaseMembers, loc);
var method = res.ResolveMember<MethodSpec> (new ResolveContext (ec.MemberContext), ref arguments);
if (method != null) {
var call = new CallEmitter ();
call.EmitPredefined (ec, method, arguments);
}
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
if (arguments.Count != 2)
throw new NotImplementedException ("arguments.Count != 2");
var concat = typeof (string).GetMethod ("Concat", new[] { typeof (object), typeof (object) });
return SLE.Expression.Add (arguments[0].Expr.MakeExpression (ctx), arguments[1].Expr.MakeExpression (ctx), concat);
}
}
//
// User-defined conditional logical operator
//
public class ConditionalLogicalOperator : UserOperatorCall
{
readonly bool is_and;
Expression oper_expr;
public ConditionalLogicalOperator (MethodSpec oper, Arguments arguments, Func<ResolveContext, Expression, Expression> expr_tree, bool is_and, Location loc)
: base (oper, arguments, expr_tree, loc)
{
this.is_and = is_and;
eclass = ExprClass.Unresolved;
}
protected override Expression DoResolve (ResolveContext ec)
{
AParametersCollection pd = oper.Parameters;
if (!TypeSpecComparer.IsEqual (type, pd.Types[0]) || !TypeSpecComparer.IsEqual (type, pd.Types[1])) {
ec.Report.Error (217, loc,
"A user-defined operator `{0}' must have parameters and return values of the same type in order to be applicable as a short circuit operator",
oper.GetSignatureForError ());
return null;
}
Expression left_dup = new EmptyExpression (type);
Expression op_true = GetOperatorTrue (ec, left_dup, loc);
Expression op_false = GetOperatorFalse (ec, left_dup, loc);
if (op_true == null || op_false == null) {
ec.Report.Error (218, loc,
"The type `{0}' must have operator `true' and operator `false' defined when `{1}' is used as a short circuit operator",
TypeManager.CSharpName (type), oper.GetSignatureForError ());
return null;
}
oper_expr = is_and ? op_false : op_true;
eclass = ExprClass.Value;
return this;
}
public override void Emit (EmitContext ec)
{
Label end_target = ec.DefineLabel ();
//
// Emit and duplicate left argument
//
bool right_contains_await = ec.HasSet (BuilderContext.Options.AsyncBody) && arguments[1].Expr.ContainsEmitWithAwait ();
if (right_contains_await) {
arguments[0] = arguments[0].EmitToField (ec);
arguments[0].Expr.Emit (ec);
} else {
arguments[0].Expr.Emit (ec);
ec.Emit (OpCodes.Dup);
arguments.RemoveAt (0);
}
oper_expr.EmitBranchable (ec, end_target, true);
base.Emit (ec);
if (right_contains_await) {
//
// Special handling when right expression contains await and left argument
// could not be left on stack before logical branch
//
Label skip_left_load = ec.DefineLabel ();
ec.Emit (OpCodes.Br_S, skip_left_load);
ec.MarkLabel (end_target);
arguments[0].Expr.Emit (ec);
ec.MarkLabel (skip_left_load);
} else {
ec.MarkLabel (end_target);
}
}
}
public class PointerArithmetic : Expression {
Expression left, right;
Binary.Operator op;
//
// We assume that `l' is always a pointer
//
public PointerArithmetic (Binary.Operator op, Expression l, Expression r, TypeSpec t, Location loc)
{
type = t;
this.loc = loc;
left = l;
right = r;
this.op = op;
}
public override bool ContainsEmitWithAwait ()
{
throw new NotImplementedException ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Error_PointerInsideExpressionTree (ec);
return null;
}
protected override Expression DoResolve (ResolveContext ec)
{
eclass = ExprClass.Variable;
var pc = left.Type as PointerContainer;
if (pc != null && pc.Element.Kind == MemberKind.Void) {
Error_VoidPointerOperation (ec);
return null;
}
return this;
}
public override void Emit (EmitContext ec)
{
TypeSpec op_type = left.Type;
// It must be either array or fixed buffer
TypeSpec element;
if (TypeManager.HasElementType (op_type)) {
element = TypeManager.GetElementType (op_type);
} else {
FieldExpr fe = left as FieldExpr;
if (fe != null)
element = ((FixedFieldSpec) (fe.Spec)).ElementType;
else
element = op_type;
}
int size = BuiltinTypeSpec.GetSize(element);
TypeSpec rtype = right.Type;
if ((op & Binary.Operator.SubtractionMask) != 0 && rtype.IsPointer){
//
// handle (pointer - pointer)
//
left.Emit (ec);
right.Emit (ec);
ec.Emit (OpCodes.Sub);
if (size != 1){
if (size == 0)
ec.Emit (OpCodes.Sizeof, element);
else
ec.EmitInt (size);
ec.Emit (OpCodes.Div);
}
ec.Emit (OpCodes.Conv_I8);
} else {
//
// handle + and - on (pointer op int)
//
Constant left_const = left as Constant;
if (left_const != null) {
//
// Optimize ((T*)null) pointer operations
//
if (left_const.IsDefaultValue) {
left = EmptyExpression.Null;
} else {
left_const = null;
}
}
left.Emit (ec);
var right_const = right as Constant;
if (right_const != null) {
//
// Optimize 0-based arithmetic
//
if (right_const.IsDefaultValue)
return;
if (size != 0)
right = new IntConstant (ec.BuiltinTypes, size, right.Location);
else
right = new SizeOf (new TypeExpression (element, right.Location), right.Location);
// TODO: Should be the checks resolve context sensitive?
ResolveContext rc = new ResolveContext (ec.MemberContext, ResolveContext.Options.UnsafeScope);
right = new Binary (Binary.Operator.Multiply, right, right_const, loc).Resolve (rc);
if (right == null)
return;
}
right.Emit (ec);
switch (rtype.BuiltinType) {
case BuiltinTypeSpec.Type.SByte:
case BuiltinTypeSpec.Type.Byte:
case BuiltinTypeSpec.Type.Short:
case BuiltinTypeSpec.Type.UShort:
ec.Emit (OpCodes.Conv_I);
break;
case BuiltinTypeSpec.Type.UInt:
ec.Emit (OpCodes.Conv_U);
break;
}
if (right_const == null && size != 1){
if (size == 0)
ec.Emit (OpCodes.Sizeof, element);
else
ec.EmitInt (size);
if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long || rtype.BuiltinType == BuiltinTypeSpec.Type.ULong)
ec.Emit (OpCodes.Conv_I8);
Binary.EmitOperatorOpcode (ec, Binary.Operator.Multiply, rtype);
}
if (left_const == null) {
if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long)
ec.Emit (OpCodes.Conv_I);
else if (rtype.BuiltinType == BuiltinTypeSpec.Type.ULong)
ec.Emit (OpCodes.Conv_U);
Binary.EmitOperatorOpcode (ec, op, op_type);
}
}
}
}
//
// A boolean-expression is an expression that yields a result
// of type bool
//
public class BooleanExpression : ShimExpression
{
public BooleanExpression (Expression expr)
: base (expr)
{
this.loc = expr.Location;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
// TODO: We should emit IsTrue (v4) instead of direct user operator
// call but that would break csc compatibility
return base.CreateExpressionTree (ec);
}
protected override Expression DoResolve (ResolveContext ec)
{
// A boolean-expression is required to be of a type
// that can be implicitly converted to bool or of
// a type that implements operator true
expr = expr.Resolve (ec);
if (expr == null)
return null;
Assign ass = expr as Assign;
if (ass != null && ass.Source is Constant) {
ec.Report.Warning (665, 3, loc,
"Assignment in conditional expression is always constant. Did you mean to use `==' instead ?");
}
if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Bool)
return expr;
if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
Arguments args = new Arguments (1);
args.Add (new Argument (expr));
return DynamicUnaryConversion.CreateIsTrue (ec, args, loc).Resolve (ec);
}
type = ec.BuiltinTypes.Bool;
Expression converted = Convert.ImplicitConversion (ec, expr, type, loc);
if (converted != null)
return converted;
//
// If no implicit conversion to bool exists, try using `operator true'
//
converted = GetOperatorTrue (ec, expr, loc);
if (converted == null) {
expr.Error_ValueCannotBeConverted (ec, loc, type, false);
return null;
}
return converted;
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public class BooleanExpressionFalse : Unary
{
public BooleanExpressionFalse (Expression expr)
: base (Operator.LogicalNot, expr, expr.Location)
{
}
protected override Expression ResolveOperator (ResolveContext ec, Expression expr)
{
return GetOperatorFalse (ec, expr, loc) ?? base.ResolveOperator (ec, expr);
}
}
/// <summary>
/// Implements the ternary conditional operator (?:)
/// </summary>
public class Conditional : Expression {
Expression expr, true_expr, false_expr;
public Conditional (Expression expr, Expression true_expr, Expression false_expr, Location loc)
{
this.expr = expr;
this.true_expr = true_expr;
this.false_expr = false_expr;
this.loc = loc;
}
#region Properties
public Expression Expr {
get {
return expr;
}
}
public Expression TrueExpr {
get {
return true_expr;
}
}
public Expression FalseExpr {
get {
return false_expr;
}
}
#endregion
public override bool ContainsEmitWithAwait ()
{
return Expr.ContainsEmitWithAwait () || true_expr.ContainsEmitWithAwait () || false_expr.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (3);
args.Add (new Argument (expr.CreateExpressionTree (ec)));
args.Add (new Argument (true_expr.CreateExpressionTree (ec)));
args.Add (new Argument (false_expr.CreateExpressionTree (ec)));
return CreateExpressionFactoryCall (ec, "Condition", args);
}
protected override Expression DoResolve (ResolveContext ec)
{
expr = expr.Resolve (ec);
//
// Unreachable code needs different resolve path. For instance for await
// expression to not generate unreachable resumable statement
//
Constant c = expr as Constant;
if (c != null && ec.CurrentBranching != null) {
bool unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
if (c.IsDefaultValue) {
ec.CurrentBranching.CurrentUsageVector.IsUnreachable = true;
true_expr = true_expr.Resolve (ec);
ec.CurrentBranching.CurrentUsageVector.IsUnreachable = unreachable;
false_expr = false_expr.Resolve (ec);
} else {
true_expr = true_expr.Resolve (ec);
ec.CurrentBranching.CurrentUsageVector.IsUnreachable = true;
false_expr = false_expr.Resolve (ec);
ec.CurrentBranching.CurrentUsageVector.IsUnreachable = unreachable;
}
} else {
true_expr = true_expr.Resolve (ec);
false_expr = false_expr.Resolve (ec);
}
if (true_expr == null || false_expr == null || expr == null)
return null;
eclass = ExprClass.Value;
TypeSpec true_type = true_expr.Type;
TypeSpec false_type = false_expr.Type;
type = true_type;
//
// First, if an implicit conversion exists from true_expr
// to false_expr, then the result type is of type false_expr.Type
//
if (!TypeSpecComparer.IsEqual (true_type, false_type)) {
Expression conv = Convert.ImplicitConversion (ec, true_expr, false_type, loc);
if (conv != null && true_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) {
//
// Check if both can convert implicitly to each other's type
//
type = false_type;
if (false_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && Convert.ImplicitConversion (ec, false_expr, true_type, loc) != null) {
ec.Report.Error (172, true_expr.Location,
"Type of conditional expression cannot be determined as `{0}' and `{1}' convert implicitly to each other",
true_type.GetSignatureForError (), false_type.GetSignatureForError ());
return null;
}
true_expr = conv;
} else if ((conv = Convert.ImplicitConversion (ec, false_expr, true_type, loc)) != null) {
false_expr = conv;
} else {
ec.Report.Error (173, true_expr.Location,
"Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
TypeManager.CSharpName (true_type), TypeManager.CSharpName (false_type));
return null;
}
}
if (c != null) {
bool is_false = c.IsDefaultValue;
//
// Don't issue the warning for constant expressions
//
if (!(is_false ? true_expr is Constant : false_expr is Constant)) {
ec.Report.Warning (429, 4, is_false ? true_expr.Location : false_expr.Location,
"Unreachable expression code detected");
}
return ReducedExpression.Create (
is_false ? false_expr : true_expr, this,
false_expr is Constant && true_expr is Constant).Resolve (ec);
}
return this;
}
public override void Emit (EmitContext ec)
{
Label false_target = ec.DefineLabel ();
Label end_target = ec.DefineLabel ();
expr.EmitBranchable (ec, false_target, false);
true_expr.Emit (ec);
ec.Emit (OpCodes.Br, end_target);
ec.MarkLabel (false_target);
false_expr.Emit (ec);
ec.MarkLabel (end_target);
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Conditional target = (Conditional) t;
target.expr = expr.Clone (clonectx);
target.true_expr = true_expr.Clone (clonectx);
target.false_expr = false_expr.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation, IVariableReference
{
LocalTemporary temp;
#region Abstract
public abstract HoistedVariable GetHoistedVariable (AnonymousExpression ae);
public abstract void SetHasAddressTaken ();
public abstract void VerifyAssigned (ResolveContext rc);
public abstract bool IsLockedByStatement { get; set; }
public abstract bool IsFixed { get; }
public abstract bool IsRef { get; }
public abstract string Name { get; }
//
// Variable IL data, it has to be protected to encapsulate hoisted variables
//
protected abstract ILocalVariable Variable { get; }
//
// Variable flow-analysis data
//
public abstract VariableInfo VariableInfo { get; }
#endregion
public virtual void AddressOf (EmitContext ec, AddressOp mode)
{
HoistedVariable hv = GetHoistedVariable (ec);
if (hv != null) {
hv.AddressOf (ec, mode);
return;
}
Variable.EmitAddressOf (ec);
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
HoistedVariable hv = GetHoistedVariable (ec);
if (hv != null)
return hv.CreateExpressionTree ();
Arguments arg = new Arguments (1);
arg.Add (new Argument (this));
return CreateExpressionFactoryCall (ec, "Constant", arg);
}
public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
{
if (IsLockedByStatement) {
rc.Report.Warning (728, 2, loc,
"Possibly incorrect assignment to `{0}' which is the argument to a using or lock statement",
Name);
}
return this;
}
public override void Emit (EmitContext ec)
{
Emit (ec, false);
}
public override void EmitSideEffect (EmitContext ec)
{
// do nothing
}
//
// This method is used by parameters that are references, that are
// being passed as references: we only want to pass the pointer (that
// is already stored in the parameter, not the address of the pointer,
// and not the value of the variable).
//
public void EmitLoad (EmitContext ec)
{
Variable.Emit (ec);
}
public void Emit (EmitContext ec, bool leave_copy)
{
HoistedVariable hv = GetHoistedVariable (ec);
if (hv != null) {
hv.Emit (ec, leave_copy);
return;
}
EmitLoad (ec);
if (IsRef) {
//
// If we are a reference, we loaded on the stack a pointer
// Now lets load the real value
//
ec.EmitLoadFromPtr (type);
}
if (leave_copy) {
ec.Emit (OpCodes.Dup);
if (IsRef) {
temp = new LocalTemporary (Type);
temp.Store (ec);
}
}
}
public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
bool prepare_for_load)
{
HoistedVariable hv = GetHoistedVariable (ec);
if (hv != null) {
hv.EmitAssign (ec, source, leave_copy, prepare_for_load);
return;
}
New n_source = source as New;
if (n_source != null) {
if (!n_source.Emit (ec, this)) {
if (leave_copy) {
EmitLoad (ec);
if (IsRef)
ec.EmitLoadFromPtr (type);
}
return;
}
} else {
if (IsRef)
EmitLoad (ec);
source.Emit (ec);
}
if (leave_copy) {
ec.Emit (OpCodes.Dup);
if (IsRef) {
temp = new LocalTemporary (Type);
temp.Store (ec);
}
}
if (IsRef)
ec.EmitStoreFromPtr (type);
else
Variable.EmitAssign (ec);
if (temp != null) {
temp.Emit (ec);
temp.Release (ec);
}
}
public override Expression EmitToField (EmitContext ec)
{
HoistedVariable hv = GetHoistedVariable (ec);
if (hv != null) {
return hv.EmitToField (ec);
}
return base.EmitToField (ec);
}
public HoistedVariable GetHoistedVariable (ResolveContext rc)
{
return GetHoistedVariable (rc.CurrentAnonymousMethod);
}
public HoistedVariable GetHoistedVariable (EmitContext ec)
{
return GetHoistedVariable (ec.CurrentAnonymousMethod);
}
public override string GetSignatureForError ()
{
return Name;
}
public bool IsHoisted {
get { return GetHoistedVariable ((AnonymousExpression) null) != null; }
}
}
//
// Resolved reference to a local variable
//
public class LocalVariableReference : VariableReference
{
public LocalVariable local_info;
public LocalVariableReference (LocalVariable li, Location l)
{
this.local_info = li;
loc = l;
}
public override VariableInfo VariableInfo {
get { return local_info.VariableInfo; }
}
public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
{
return local_info.HoistedVariant;
}
#region Properties
//
// A local variable is always fixed
//
public override bool IsFixed {
get {
return true;
}
}
public override bool IsLockedByStatement {
get {
return local_info.IsLocked;
}
set {
local_info.IsLocked = value;
}
}
public override bool IsRef {
get { return false; }
}
public override string Name {
get { return local_info.Name; }
}
#endregion
public override void VerifyAssigned (ResolveContext rc)
{
VariableInfo variable_info = local_info.VariableInfo;
if (variable_info == null)
return;
if (variable_info.IsAssigned (rc))
return;
rc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name);
variable_info.SetAssigned (rc);
}
public override void SetHasAddressTaken ()
{
local_info.SetHasAddressTaken ();
}
void DoResolveBase (ResolveContext ec)
{
//
// If we are referencing a variable from the external block
// flag it for capturing
//
if (ec.MustCaptureVariable (local_info)) {
if (local_info.AddressTaken) {
AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc);
} else if (local_info.IsFixed) {
ec.Report.Error (1764, loc,
"Cannot use fixed local `{0}' inside an anonymous method, lambda expression or query expression",
GetSignatureForError ());
}
if (ec.IsVariableCapturingRequired) {
AnonymousMethodStorey storey = local_info.Block.Explicit.CreateAnonymousMethodStorey (ec);
storey.CaptureLocalVariable (ec, local_info);
}
}
eclass = ExprClass.Variable;
type = local_info.Type;
}
protected override Expression DoResolve (ResolveContext ec)
{
local_info.SetIsUsed ();
VerifyAssigned (ec);
DoResolveBase (ec);
return this;
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
// is out param
if (right_side == EmptyExpression.OutAccess)
local_info.SetIsUsed ();
if (local_info.IsReadonly && !ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.UsingInitializerScope)) {
int code;
string msg;
if (right_side == EmptyExpression.OutAccess) {
code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
} else if (right_side == EmptyExpression.LValueMemberAccess) {
code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
} else if (right_side == EmptyExpression.LValueMemberOutAccess) {
code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
} else if (right_side == EmptyExpression.UnaryAddress) {
code = 459; msg = "Cannot take the address of {1} `{0}'";
} else {
code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
}
ec.Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
} else if (VariableInfo != null) {
VariableInfo.SetAssigned (ec);
}
if (eclass == ExprClass.Unresolved)
DoResolveBase (ec);
return base.DoResolveLValue (ec, right_side);
}
public override int GetHashCode ()
{
return local_info.GetHashCode ();
}
public override bool Equals (object obj)
{
LocalVariableReference lvr = obj as LocalVariableReference;
if (lvr == null)
return false;
return local_info == lvr.local_info;
}
protected override ILocalVariable Variable {
get { return local_info; }
}
public override string ToString ()
{
return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
// Nothing
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// This represents a reference to a parameter in the intermediate
/// representation.
/// </summary>
public class ParameterReference : VariableReference
{
protected ParametersBlock.ParameterInfo pi;
public ParameterReference (ParametersBlock.ParameterInfo pi, Location loc)
{
this.pi = pi;
this.loc = loc;
}
#region Properties
public override bool IsLockedByStatement {
get {
return pi.IsLocked;
}
set {
pi.IsLocked = value;
}
}
public override bool IsRef {
get { return (pi.Parameter.ModFlags & Parameter.Modifier.ISBYREF) != 0; }
}
bool HasOutModifier {
get { return pi.Parameter.ModFlags == Parameter.Modifier.OUT; }
}
public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
{
return pi.Parameter.HoistedVariant;
}
//
// A ref or out parameter is classified as a moveable variable, even
// if the argument given for the parameter is a fixed variable
//
public override bool IsFixed {
get { return !IsRef; }
}
public override string Name {
get { return Parameter.Name; }
}
public Parameter Parameter {
get { return pi.Parameter; }
}
public override VariableInfo VariableInfo {
get { return pi.VariableInfo; }
}
protected override ILocalVariable Variable {
get { return Parameter; }
}
#endregion
public override void AddressOf (EmitContext ec, AddressOp mode)
{
//
// ParameterReferences might already be a reference
//
if (IsRef) {
EmitLoad (ec);
return;
}
base.AddressOf (ec, mode);
}
public override void SetHasAddressTaken ()
{
Parameter.HasAddressTaken = true;
}
void SetAssigned (ResolveContext ec)
{
if (HasOutModifier && ec.DoFlowAnalysis)
ec.CurrentBranching.SetAssigned (VariableInfo);
}
bool DoResolveBase (ResolveContext ec)
{
if (eclass != ExprClass.Unresolved)
return true;
type = pi.ParameterType;
eclass = ExprClass.Variable;
//
// If we are referencing a parameter from the external block
// flag it for capturing
//
if (ec.MustCaptureVariable (pi)) {
if (Parameter.HasAddressTaken)
AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc);
if (IsRef) {
ec.Report.Error (1628, loc,
"Parameter `{0}' cannot be used inside `{1}' when using `ref' or `out' modifier",
Name, ec.CurrentAnonymousMethod.ContainerType);
}
if (ec.IsVariableCapturingRequired && !pi.Block.ParametersBlock.IsExpressionTree) {
AnonymousMethodStorey storey = pi.Block.Explicit.CreateAnonymousMethodStorey (ec);
storey.CaptureParameter (ec, this);
}
}
return true;
}
public override int GetHashCode ()
{
return Name.GetHashCode ();
}
public override bool Equals (object obj)
{
ParameterReference pr = obj as ParameterReference;
if (pr == null)
return false;
return Name == pr.Name;
}
protected override void CloneTo (CloneContext clonectx, Expression target)
{
// Nothing to clone
return;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
HoistedVariable hv = GetHoistedVariable (ec);
if (hv != null)
return hv.CreateExpressionTree ();
return Parameter.ExpressionTreeVariableReference ();
}
//
// Notice that for ref/out parameters, the type exposed is not the
// same type exposed externally.
//
// for "ref int a":
// externally we expose "int&"
// here we expose "int".
//
// We record this in "is_ref". This means that the type system can treat
// the type as it is expected, but when we generate the code, we generate
// the alternate kind of code.
//
protected override Expression DoResolve (ResolveContext ec)
{
if (!DoResolveBase (ec))
return null;
VerifyAssigned (ec);
return this;
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
if (!DoResolveBase (ec))
return null;
SetAssigned (ec);
return base.DoResolveLValue (ec, right_side);
}
public override void VerifyAssigned (ResolveContext rc)
{
// HACK: Variables are not captured in probing mode
if (rc.IsInProbingMode)
return;
if (HasOutModifier && !VariableInfo.IsAssigned (rc)) {
rc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
}
}
}
/// <summary>
/// Invocation of methods or delegates.
/// </summary>
public class Invocation : ExpressionStatement
{
protected Arguments arguments;
protected Expression expr;
protected MethodGroupExpr mg;
//
// arguments is an ArrayList, but we do not want to typecast,
// as it might be null.
//
public Invocation (Expression expr, Arguments arguments)
{
this.expr = expr;
this.arguments = arguments;
if (expr != null)
loc = expr.Location;
}
#region Properties
public Arguments Arguments {
get {
return arguments;
}
}
public Expression Exp {
get {
return expr;
}
}
#endregion
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Invocation target = (Invocation) t;
if (arguments != null)
target.arguments = arguments.Clone (clonectx);
target.expr = expr.Clone (clonectx);
}
public override bool ContainsEmitWithAwait ()
{
if (arguments != null && arguments.ContainsEmitWithAwait ())
return true;
return mg.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Expression instance = mg.IsInstance ?
mg.InstanceExpression.CreateExpressionTree (ec) :
new NullLiteral (loc);
var args = Arguments.CreateForExpressionTree (ec, arguments,
instance,
mg.CreateExpressionTree (ec));
return CreateExpressionFactoryCall (ec, "Call", args);
}
protected override Expression DoResolve (ResolveContext ec)
{
Expression member_expr;
var atn = expr as ATypeNameExpression;
if (atn != null) {
member_expr = atn.LookupNameExpression (ec, MemberLookupRestrictions.InvocableOnly | MemberLookupRestrictions.ReadAccess);
if (member_expr != null)
member_expr = member_expr.Resolve (ec);
} else {
member_expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
}
if (member_expr == null)
return null;
//
// Next, evaluate all the expressions in the argument list
//
bool dynamic_arg = false;
if (arguments != null)
arguments.Resolve (ec, out dynamic_arg);
TypeSpec expr_type = member_expr.Type;
if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
return DoResolveDynamic (ec, member_expr);
mg = member_expr as MethodGroupExpr;
Expression invoke = null;
if (mg == null) {
if (expr_type != null && expr_type.IsDelegate) {
invoke = new DelegateInvocation (member_expr, arguments, loc);
invoke = invoke.Resolve (ec);
if (invoke == null || !dynamic_arg)
return invoke;
} else {
if (member_expr is RuntimeValueExpression) {
ec.Report.Error (Report.RuntimeErrorId, loc, "Cannot invoke a non-delegate type `{0}'",
member_expr.Type.GetSignatureForError ()); ;
return null;
}
MemberExpr me = member_expr as MemberExpr;
if (me == null) {
member_expr.Error_UnexpectedKind (ec, ResolveFlags.MethodGroup, loc);
return null;
}
ec.Report.Error (1955, loc, "The member `{0}' cannot be used as method or delegate",
member_expr.GetSignatureForError ());
return null;
}
}
if (invoke == null) {
mg = DoResolveOverload (ec);
if (mg == null)
return null;
}
if (dynamic_arg)
return DoResolveDynamic (ec, member_expr);
var method = mg.BestCandidate;
type = mg.BestCandidateReturnType;
if (arguments == null && method.DeclaringType.BuiltinType == BuiltinTypeSpec.Type.Object && method.Name == Destructor.MetadataName) {
if (mg.IsBase)
ec.Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
else
ec.Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
return null;
}
IsSpecialMethodInvocation (ec, method, loc);
eclass = ExprClass.Value;
return this;
}
protected virtual Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr)
{
Arguments args;
DynamicMemberBinder dmb = memberExpr as DynamicMemberBinder;
if (dmb != null) {
args = dmb.Arguments;
if (arguments != null)
args.AddRange (arguments);
} else if (mg == null) {
if (arguments == null)
args = new Arguments (1);
else
args = arguments;
args.Insert (0, new Argument (memberExpr));
this.expr = null;
} else {
if (mg.IsBase) {
ec.Report.Error (1971, loc,
"The base call to method `{0}' cannot be dynamically dispatched. Consider casting the dynamic arguments or eliminating the base access",
mg.Name);
return null;
}
if (arguments == null)
args = new Arguments (1);
else
args = arguments;
MemberAccess ma = expr as MemberAccess;
if (ma != null) {
var left_type = ma.LeftExpression as TypeExpr;
if (left_type != null) {
args.Insert (0, new Argument (new TypeOf (left_type.Type, loc).Resolve (ec), Argument.AType.DynamicTypeName));
} else {
//
// Any value type has to be pass as by-ref to get back the same
// instance on which the member was called
//
var mod = ma.LeftExpression is IMemoryLocation && TypeSpec.IsValueType (ma.LeftExpression.Type) ?
Argument.AType.Ref : Argument.AType.None;
args.Insert (0, new Argument (ma.LeftExpression.Resolve (ec), mod));
}
} else { // is SimpleName
if (ec.IsStatic) {
args.Insert (0, new Argument (new TypeOf (ec.CurrentType, loc).Resolve (ec), Argument.AType.DynamicTypeName));
} else {
args.Insert (0, new Argument (new This (loc).Resolve (ec)));
}
}
}
return new DynamicInvocation (expr as ATypeNameExpression, args, loc).Resolve (ec);
}
protected virtual MethodGroupExpr DoResolveOverload (ResolveContext ec)
{
return mg.OverloadResolve (ec, ref arguments, null, OverloadResolver.Restrictions.None);
}
static MetaType[] GetVarargsTypes (MethodSpec mb, Arguments arguments)
{
AParametersCollection pd = mb.Parameters;
Argument a = arguments[pd.Count - 1];
Arglist list = (Arglist) a.Expr;
return list.ArgumentTypes;
}
public override string GetSignatureForError ()
{
return mg.GetSignatureForError ();
}
//
// If a member is a method or event, or if it is a constant, field or property of either a delegate type
// or the type dynamic, then the member is invocable
//
public static bool IsMemberInvocable (MemberSpec member)
{
switch (member.Kind) {
case MemberKind.Event:
return true;
case MemberKind.Field:
case MemberKind.Property:
var m = member as IInterfaceMemberSpec;
return m.MemberType.IsDelegate || m.MemberType.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
default:
return false;
}
}
public static bool IsSpecialMethodInvocation (ResolveContext ec, MethodSpec method, Location loc)
{
if (!method.IsReservedMethod)
return false;
if (ec.HasSet (ResolveContext.Options.InvokeSpecialName) || ec.CurrentMemberDefinition.IsCompilerGenerated)
return false;
ec.Report.SymbolRelatedToPreviousError (method);
ec.Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
method.GetSignatureForError ());
return true;
}
public override void Emit (EmitContext ec)
{
mg.EmitCall (ec, arguments);
}
public override void EmitStatement (EmitContext ec)
{
Emit (ec);
//
// Pop the return value if there is one
//
if (type.Kind != MemberKind.Void)
ec.Emit (OpCodes.Pop);
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
return MakeExpression (ctx, mg.InstanceExpression, mg.BestCandidate, arguments);
}
public static SLE.Expression MakeExpression (BuilderContext ctx, Expression instance, MethodSpec mi, Arguments args)
{
#if STATIC
throw new NotSupportedException ();
#else
var instance_expr = instance == null ? null : instance.MakeExpression (ctx);
return SLE.Expression.Call (instance_expr, (MethodInfo) mi.GetMetaInfo (), Arguments.MakeExpression (args, ctx));
#endif
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
//
// Implements simple new expression
//
public class New : ExpressionStatement, IMemoryLocation
{
protected Arguments arguments;
//
// During bootstrap, it contains the RequestedType,
// but if `type' is not null, it *might* contain a NewDelegate
// (because of field multi-initialization)
//
protected Expression RequestedType;
protected MethodSpec method;
public New (Expression requested_type, Arguments arguments, Location l)
{
RequestedType = requested_type;
this.arguments = arguments;
loc = l;
}
#region Properties
public Arguments Arguments {
get {
return arguments;
}
}
public Expression TypeRequested {
get {
return RequestedType;
}
}
//
// Returns true for resolved `new S()'
//
public bool IsDefaultStruct {
get {
return arguments == null && type.IsStruct && GetType () == typeof (New);
}
}
public Expression TypeExpression {
get {
return RequestedType;
}
}
#endregion
/// <summary>
/// Converts complex core type syntax like 'new int ()' to simple constant
/// </summary>
public static Constant Constantify (TypeSpec t, Location loc)
{
switch (t.BuiltinType) {
case BuiltinTypeSpec.Type.Int:
return new IntConstant (t, 0, loc);
case BuiltinTypeSpec.Type.UInt:
return new UIntConstant (t, 0, loc);
case BuiltinTypeSpec.Type.Long:
return new LongConstant (t, 0, loc);
case BuiltinTypeSpec.Type.ULong:
return new ULongConstant (t, 0, loc);
case BuiltinTypeSpec.Type.Float:
return new FloatConstant (t, 0, loc);
case BuiltinTypeSpec.Type.Double:
return new DoubleConstant (t, 0, loc);
case BuiltinTypeSpec.Type.Short:
return new ShortConstant (t, 0, loc);
case BuiltinTypeSpec.Type.UShort:
return new UShortConstant (t, 0, loc);
case BuiltinTypeSpec.Type.SByte:
return new SByteConstant (t, 0, loc);
case BuiltinTypeSpec.Type.Byte:
return new ByteConstant (t, 0, loc);
case BuiltinTypeSpec.Type.Char:
return new CharConstant (t, '\0', loc);
case BuiltinTypeSpec.Type.Bool:
return new BoolConstant (t, false, loc);
case BuiltinTypeSpec.Type.Decimal:
return new DecimalConstant (t, 0, loc);
}
if (t.IsEnum)
return new EnumConstant (Constantify (EnumSpec.GetUnderlyingType (t), loc), t);
if (t.IsNullableType)
return Nullable.LiftedNull.Create (t, loc);
return null;
}
public override bool ContainsEmitWithAwait ()
{
return arguments != null && arguments.ContainsEmitWithAwait ();
}
//
// Checks whether the type is an interface that has the
// [ComImport, CoClass] attributes and must be treated
// specially
//
public Expression CheckComImport (ResolveContext ec)
{
if (!type.IsInterface)
return null;
//
// Turn the call into:
// (the-interface-stated) (new class-referenced-in-coclassattribute ())
//
var real_class = type.MemberDefinition.GetAttributeCoClass ();
if (real_class == null)
return null;
New proxy = new New (new TypeExpression (real_class, loc), arguments, loc);
Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
return cast.Resolve (ec);
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args;
if (method == null) {
args = new Arguments (1);
args.Add (new Argument (new TypeOf (type, loc)));
} else {
args = Arguments.CreateForExpressionTree (ec,
arguments, new TypeOfMethod (method, loc));
}
return CreateExpressionFactoryCall (ec, "New", args);
}
protected override Expression DoResolve (ResolveContext ec)
{
type = RequestedType.ResolveAsType (ec);
if (type == null)
return null;
eclass = ExprClass.Value;
if (type.IsPointer) {
ec.Report.Error (1919, loc, "Unsafe type `{0}' cannot be used in an object creation expression",
TypeManager.CSharpName (type));
return null;
}
if (arguments == null) {
Constant c = Constantify (type, RequestedType.Location);
if (c != null)
return ReducedExpression.Create (c, this);
}
if (type.IsDelegate) {
return (new NewDelegate (type, arguments, loc)).Resolve (ec);
}
var tparam = type as TypeParameterSpec;
if (tparam != null) {
//
// Check whether the type of type parameter can be constructed. BaseType can be a struct for method overrides
// where type parameter constraint is inflated to struct
//
if ((tparam.SpecialConstraint & (SpecialConstraint.Struct | SpecialConstraint.Constructor)) == 0 && !TypeSpec.IsValueType (tparam)) {
ec.Report.Error (304, loc,
"Cannot create an instance of the variable type `{0}' because it does not have the new() constraint",
TypeManager.CSharpName (type));
}
if ((arguments != null) && (arguments.Count != 0)) {
ec.Report.Error (417, loc,
"`{0}': cannot provide arguments when creating an instance of a variable type",
TypeManager.CSharpName (type));
}
return this;
}
if (type.IsStatic) {
ec.Report.SymbolRelatedToPreviousError (type);
ec.Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
return null;
}
if (type.IsInterface || type.IsAbstract){
if (!TypeManager.IsGenericType (type)) {
RequestedType = CheckComImport (ec);
if (RequestedType != null)
return RequestedType;
}
ec.Report.SymbolRelatedToPreviousError (type);
ec.Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
return null;
}
//
// Any struct always defines parameterless constructor
//
if (type.IsStruct && arguments == null)
return this;
bool dynamic;
if (arguments != null) {
arguments.Resolve (ec, out dynamic);
} else {
dynamic = false;
}
method = ConstructorLookup (ec, type, ref arguments, loc);
if (dynamic) {
arguments.Insert (0, new Argument (new TypeOf (type, loc).Resolve (ec), Argument.AType.DynamicTypeName));
return new DynamicConstructorBinder (type, arguments, loc).Resolve (ec);
}
return this;
}
bool DoEmitTypeParameter (EmitContext ec)
{
var m = ec.Module.PredefinedMembers.ActivatorCreateInstance.Resolve (loc);
if (m == null)
return true;
var ctor_factory = m.MakeGenericMethod (ec.MemberContext, type);
var tparam = (TypeParameterSpec) type;
if (tparam.IsReferenceType) {
ec.Emit (OpCodes.Call, ctor_factory);
return true;
}
// Allow DoEmit() to be called multiple times.
// We need to create a new LocalTemporary each time since
// you can't share LocalBuilders among ILGeneators.
LocalTemporary temp = new LocalTemporary (type);
Label label_activator = ec.DefineLabel ();
Label label_end = ec.DefineLabel ();
temp.AddressOf (ec, AddressOp.Store);
ec.Emit (OpCodes.Initobj, type);
temp.Emit (ec);
ec.Emit (OpCodes.Box, type);
ec.Emit (OpCodes.Brfalse, label_activator);
temp.AddressOf (ec, AddressOp.Store);
ec.Emit (OpCodes.Initobj, type);
temp.Emit (ec);
temp.Release (ec);
ec.Emit (OpCodes.Br_S, label_end);
ec.MarkLabel (label_activator);
ec.Emit (OpCodes.Call, ctor_factory);
ec.MarkLabel (label_end);
return true;
}
//
// This Emit can be invoked in two contexts:
// * As a mechanism that will leave a value on the stack (new object)
// * As one that wont (init struct)
//
// If we are dealing with a ValueType, we have a few
// situations to deal with:
//
// * The target is a ValueType, and we have been provided
// the instance (this is easy, we are being assigned).
//
// * The target of New is being passed as an argument,
// to a boxing operation or a function that takes a
// ValueType.
//
// In this case, we need to create a temporary variable
// that is the argument of New.
//
// Returns whether a value is left on the stack
//
// *** Implementation note ***
//
// To benefit from this optimization, each assignable expression
// has to manually cast to New and call this Emit.
//
// TODO: It's worth to implement it for arrays and fields
//
public virtual bool Emit (EmitContext ec, IMemoryLocation target)
{
bool is_value_type = TypeSpec.IsValueType (type);
VariableReference vr = target as VariableReference;
if (target != null && is_value_type && (vr != null || method == null)) {
target.AddressOf (ec, AddressOp.Store);
} else if (vr != null && vr.IsRef) {
vr.EmitLoad (ec);
}
if (arguments != null) {
if (ec.HasSet (BuilderContext.Options.AsyncBody) && (arguments.Count > (this is NewInitialize ? 0 : 1)) && arguments.ContainsEmitWithAwait ())
arguments = arguments.Emit (ec, false, true);
arguments.Emit (ec);
}
if (is_value_type) {
if (method == null) {
ec.Emit (OpCodes.Initobj, type);
return false;
}
if (vr != null) {
ec.Emit (OpCodes.Call, method);
return false;
}
}
if (type is TypeParameterSpec)
return DoEmitTypeParameter (ec);
ec.Emit (OpCodes.Newobj, method);
return true;
}
public override void Emit (EmitContext ec)
{
LocalTemporary v = null;
if (method == null && TypeSpec.IsValueType (type)) {
// TODO: Use temporary variable from pool
v = new LocalTemporary (type);
}
if (!Emit (ec, v))
v.Emit (ec);
}
public override void EmitStatement (EmitContext ec)
{
LocalTemporary v = null;
if (method == null && TypeSpec.IsValueType (type)) {
// TODO: Use temporary variable from pool
v = new LocalTemporary (type);
}
if (Emit (ec, v))
ec.Emit (OpCodes.Pop);
}
public void AddressOf (EmitContext ec, AddressOp mode)
{
EmitAddressOf (ec, mode);
}
protected virtual IMemoryLocation EmitAddressOf (EmitContext ec, AddressOp mode)
{
LocalTemporary value_target = new LocalTemporary (type);
if (type is TypeParameterSpec) {
DoEmitTypeParameter (ec);
value_target.Store (ec);
value_target.AddressOf (ec, mode);
return value_target;
}
value_target.AddressOf (ec, AddressOp.Store);
if (method == null) {
ec.Emit (OpCodes.Initobj, type);
} else {
if (arguments != null)
arguments.Emit (ec);
ec.Emit (OpCodes.Call, method);
}
value_target.AddressOf (ec, mode);
return value_target;
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
New target = (New) t;
target.RequestedType = RequestedType.Clone (clonectx);
if (arguments != null){
target.arguments = arguments.Clone (clonectx);
}
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
#if STATIC
return base.MakeExpression (ctx);
#else
return SLE.Expression.New ((ConstructorInfo) method.GetMetaInfo (), Arguments.MakeExpression (arguments, ctx));
#endif
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
//
// Array initializer expression, the expression is allowed in
// variable or field initialization only which makes it tricky as
// the type has to be infered based on the context either from field
// type or variable type (think of multiple declarators)
//
public class ArrayInitializer : Expression
{
List<Expression> elements;
BlockVariableDeclaration variable;
public ArrayInitializer (List<Expression> init, Location loc)
{
elements = init;
this.loc = loc;
}
public ArrayInitializer (int count, Location loc)
: this (new List<Expression> (count), loc)
{
}
public ArrayInitializer (Location loc)
: this (4, loc)
{
}
#region Properties
public int Count {
get { return elements.Count; }
}
public List<Expression> Elements {
get {
return elements;
}
}
public Expression this [int index] {
get {
return elements [index];
}
}
public BlockVariableDeclaration VariableDeclaration {
get {
return variable;
}
set {
variable = value;
}
}
#endregion
public void Add (Expression expr)
{
elements.Add (expr);
}
public override bool ContainsEmitWithAwait ()
{
throw new NotSupportedException ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
throw new NotSupportedException ("ET");
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
var target = (ArrayInitializer) t;
target.elements = new List<Expression> (elements.Count);
foreach (var element in elements)
target.elements.Add (element.Clone (clonectx));
}
protected override Expression DoResolve (ResolveContext rc)
{
var current_field = rc.CurrentMemberDefinition as FieldBase;
TypeExpression type;
if (current_field != null && rc.CurrentAnonymousMethod == null) {
type = new TypeExpression (current_field.MemberType, current_field.Location);
} else if (variable != null) {
if (variable.TypeExpression is VarExpr) {
rc.Report.Error (820, loc, "An implicitly typed local variable declarator cannot use an array initializer");
return EmptyExpression.Null;
}
type = new TypeExpression (variable.Variable.Type, variable.Variable.Location);
} else {
throw new NotImplementedException ("Unexpected array initializer context");
}
return new ArrayCreation (type, this).Resolve (rc);
}
public override void Emit (EmitContext ec)
{
throw new InternalErrorException ("Missing Resolve call");
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// 14.5.10.2: Represents an array creation expression.
/// </summary>
///
/// <remarks>
/// There are two possible scenarios here: one is an array creation
/// expression that specifies the dimensions and optionally the
/// initialization data and the other which does not need dimensions
/// specified but where initialization data is mandatory.
/// </remarks>
public class ArrayCreation : Expression
{
FullNamedExpression requested_base_type;
ArrayInitializer initializers;
//
// The list of Argument types.
// This is used to construct the `newarray' or constructor signature
//
protected List<Expression> arguments;
protected TypeSpec array_element_type;
int num_arguments = 0;
protected int dimensions;
protected readonly ComposedTypeSpecifier rank;
Expression first_emit;
LocalTemporary first_emit_temp;
protected List<Expression> array_data;
Dictionary<int, int> bounds;
// The number of constants in array initializers
int const_initializers_count;
bool only_constant_initializers;
public List<Expression> Arguments {
get { return this.arguments; }
}
public FullNamedExpression NewType {
get { return this.requested_base_type; }
}
public ArrayCreation (FullNamedExpression requested_base_type, List<Expression> exprs, ComposedTypeSpecifier rank, ArrayInitializer initializers, Location l)
: this (requested_base_type, rank, initializers, l)
{
arguments = exprs;
num_arguments = arguments.Count;
}
//
// For expressions like int[] foo = new int[] { 1, 2, 3 };
//
public ArrayCreation (FullNamedExpression requested_base_type, ComposedTypeSpecifier rank, ArrayInitializer initializers, Location loc)
{
this.requested_base_type = requested_base_type;
this.rank = rank;
this.initializers = initializers;
this.loc = loc;
if (rank != null)
num_arguments = rank.Dimension;
}
//
// For compiler generated single dimensional arrays only
//
public ArrayCreation (FullNamedExpression requested_base_type, ArrayInitializer initializers, Location loc)
: this (requested_base_type, ComposedTypeSpecifier.SingleDimension, initializers, loc)
{
}
//
// For expressions like int[] foo = { 1, 2, 3 };
//
public ArrayCreation (FullNamedExpression requested_base_type, ArrayInitializer initializers)
: this (requested_base_type, null, initializers, initializers.Location)
{
}
public ComposedTypeSpecifier Rank {
get {
return this.rank;
}
}
public FullNamedExpression TypeExpression {
get {
return this.requested_base_type;
}
}
public ArrayInitializer Initializers {
get {
return this.initializers;
}
}
bool CheckIndices (ResolveContext ec, ArrayInitializer probe, int idx, bool specified_dims, int child_bounds)
{
if (initializers != null && bounds == null) {
//
// We use this to store all the date values in the order in which we
// will need to store them in the byte blob later
//
array_data = new List<Expression> ();
bounds = new Dictionary<int, int> ();
}
if (specified_dims) {
Expression a = arguments [idx];
a = a.Resolve (ec);
if (a == null)
return false;
a = ConvertExpressionToArrayIndex (ec, a);
if (a == null)
return false;
arguments[idx] = a;
if (initializers != null) {
Constant c = a as Constant;
if (c == null && a is ArrayIndexCast)
c = ((ArrayIndexCast) a).Child as Constant;
if (c == null) {
ec.Report.Error (150, a.Location, "A constant value is expected");
return false;
}
int value;
try {
value = System.Convert.ToInt32 (c.GetValue ());
} catch {
ec.Report.Error (150, a.Location, "A constant value is expected");
return false;
}
// TODO: probe.Count does not fit ulong in
if (value != probe.Count) {
ec.Report.Error (847, loc, "An array initializer of length `{0}' was expected", value.ToString ());
return false;
}
bounds[idx] = value;
}
}
if (initializers == null)
return true;
for (int i = 0; i < probe.Count; ++i) {
var o = probe [i];
if (o is ArrayInitializer) {
var sub_probe = o as ArrayInitializer;
if (idx + 1 >= dimensions){
ec.Report.Error (623, loc, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
return false;
}
bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims, child_bounds - 1);
if (!ret)
return false;
} else if (child_bounds > 1) {
ec.Report.Error (846, o.Location, "A nested array initializer was expected");
} else {
Expression element = ResolveArrayElement (ec, o);
if (element == null)
continue;
// Initializers with the default values can be ignored
Constant c = element as Constant;
if (c != null) {
if (!c.IsDefaultInitializer (array_element_type)) {
++const_initializers_count;
}
} else {
only_constant_initializers = false;
}
array_data.Add (element);
}
}
return true;
}
public override bool ContainsEmitWithAwait ()
{
foreach (var arg in arguments) {
if (arg.ContainsEmitWithAwait ())
return true;
}
return InitializersContainAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args;
if (array_data == null) {
args = new Arguments (arguments.Count + 1);
args.Add (new Argument (new TypeOf (array_element_type, loc)));
foreach (Expression a in arguments)
args.Add (new Argument (a.CreateExpressionTree (ec)));
return CreateExpressionFactoryCall (ec, "NewArrayBounds", args);
}
if (dimensions > 1) {
ec.Report.Error (838, loc, "An expression tree cannot contain a multidimensional array initializer");
return null;
}
args = new Arguments (array_data == null ? 1 : array_data.Count + 1);
args.Add (new Argument (new TypeOf (array_element_type, loc)));
if (array_data != null) {
for (int i = 0; i < array_data.Count; ++i) {
Expression e = array_data [i];
args.Add (new Argument (e.CreateExpressionTree (ec)));
}
}
return CreateExpressionFactoryCall (ec, "NewArrayInit", args);
}
void UpdateIndices (ResolveContext rc)
{
int i = 0;
for (var probe = initializers; probe != null;) {
Expression e = new IntConstant (rc.BuiltinTypes, probe.Count, Location.Null);
arguments.Add (e);
bounds[i++] = probe.Count;
if (probe.Count > 0 && probe [0] is ArrayInitializer) {
probe = (ArrayInitializer) probe[0];
} else if (dimensions > i) {
continue;
} else {
return;
}
}
}
protected override void Error_NegativeArrayIndex (ResolveContext ec, Location loc)
{
ec.Report.Error (248, loc, "Cannot create an array with a negative size");
}
bool InitializersContainAwait ()
{
if (array_data == null)
return false;
foreach (var expr in array_data) {
if (expr.ContainsEmitWithAwait ())
return true;
}
return false;
}
protected virtual Expression ResolveArrayElement (ResolveContext ec, Expression element)
{
element = element.Resolve (ec);
if (element == null)
return null;
if (element is CompoundAssign.TargetExpression) {
if (first_emit != null)
throw new InternalErrorException ("Can only handle one mutator at a time");
first_emit = element;
element = first_emit_temp = new LocalTemporary (element.Type);
}
return Convert.ImplicitConversionRequired (
ec, element, array_element_type, loc);
}
protected bool ResolveInitializers (ResolveContext ec)
{
only_constant_initializers = true;
if (arguments != null) {
bool res = true;
for (int i = 0; i < arguments.Count; ++i) {
res &= CheckIndices (ec, initializers, i, true, dimensions);
if (initializers != null)
break;
}
return res;
}
arguments = new List<Expression> ();
if (!CheckIndices (ec, initializers, 0, false, dimensions))
return false;
UpdateIndices (ec);
return true;
}
//
// Resolved the type of the array
//
bool ResolveArrayType (ResolveContext ec)
{
//
// Lookup the type
//
FullNamedExpression array_type_expr;
if (num_arguments > 0) {
array_type_expr = new ComposedCast (requested_base_type, rank);
} else {
array_type_expr = requested_base_type;
}
type = array_type_expr.ResolveAsType (ec);
if (array_type_expr == null)
return false;
var ac = type as ArrayContainer;
if (ac == null) {
ec.Report.Error (622, loc, "Can only use array initializer expressions to assign to array types. Try using a new expression instead");
return false;
}
array_element_type = ac.Element;
dimensions = ac.Rank;
return true;
}
protected override Expression DoResolve (ResolveContext ec)
{
if (type != null)
return this;
if (!ResolveArrayType (ec))
return null;
//
// validate the initializers and fill in any missing bits
//
if (!ResolveInitializers (ec))
return null;
eclass = ExprClass.Value;
return this;
}
byte [] MakeByteBlob ()
{
int factor;
byte [] data;
byte [] element;
int count = array_data.Count;
TypeSpec element_type = array_element_type;
if (element_type.IsEnum)
element_type = EnumSpec.GetUnderlyingType (element_type);
factor = BuiltinTypeSpec.GetSize (element_type);
if (factor == 0)
throw new Exception ("unrecognized type in MakeByteBlob: " + element_type);
data = new byte [(count * factor + 3) & ~3];
int idx = 0;
for (int i = 0; i < count; ++i) {
var c = array_data[i] as Constant;
if (c == null) {
idx += factor;
continue;
}
object v = c.GetValue ();
switch (element_type.BuiltinType) {
case BuiltinTypeSpec.Type.Long:
long lval = (long) v;
for (int j = 0; j < factor; ++j) {
data[idx + j] = (byte) (lval & 0xFF);
lval = (lval >> 8);
}
break;
case BuiltinTypeSpec.Type.ULong:
ulong ulval = (ulong) v;
for (int j = 0; j < factor; ++j) {
data[idx + j] = (byte) (ulval & 0xFF);
ulval = (ulval >> 8);
}
break;
case BuiltinTypeSpec.Type.Float:
element = BitConverter.GetBytes ((float) v);
for (int j = 0; j < factor; ++j)
data[idx + j] = element[j];
if (!BitConverter.IsLittleEndian)
System.Array.Reverse (data, idx, 4);
break;
case BuiltinTypeSpec.Type.Double:
element = BitConverter.GetBytes ((double) v);
for (int j = 0; j < factor; ++j)
data[idx + j] = element[j];
// FIXME: Handle the ARM float format.
if (!BitConverter.IsLittleEndian)
System.Array.Reverse (data, idx, 8);
break;
case BuiltinTypeSpec.Type.Char:
int chval = (int) ((char) v);
data[idx] = (byte) (chval & 0xff);
data[idx + 1] = (byte) (chval >> 8);
break;
case BuiltinTypeSpec.Type.Short:
int sval = (int) ((short) v);
data[idx] = (byte) (sval & 0xff);
data[idx + 1] = (byte) (sval >> 8);
break;
case BuiltinTypeSpec.Type.UShort:
int usval = (int) ((ushort) v);
data[idx] = (byte) (usval & 0xff);
data[idx + 1] = (byte) (usval >> 8);
break;
case BuiltinTypeSpec.Type.Int:
int val = (int) v;
data[idx] = (byte) (val & 0xff);
data[idx + 1] = (byte) ((val >> 8) & 0xff);
data[idx + 2] = (byte) ((val >> 16) & 0xff);
data[idx + 3] = (byte) (val >> 24);
break;
case BuiltinTypeSpec.Type.UInt:
uint uval = (uint) v;
data[idx] = (byte) (uval & 0xff);
data[idx + 1] = (byte) ((uval >> 8) & 0xff);
data[idx + 2] = (byte) ((uval >> 16) & 0xff);
data[idx + 3] = (byte) (uval >> 24);
break;
case BuiltinTypeSpec.Type.SByte:
data[idx] = (byte) (sbyte) v;
break;
case BuiltinTypeSpec.Type.Byte:
data[idx] = (byte) v;
break;
case BuiltinTypeSpec.Type.Bool:
data[idx] = (byte) ((bool) v ? 1 : 0);
break;
case BuiltinTypeSpec.Type.Decimal:
int[] bits = Decimal.GetBits ((decimal) v);
int p = idx;
// FIXME: For some reason, this doesn't work on the MS runtime.
int[] nbits = new int[4];
nbits[0] = bits[3];
nbits[1] = bits[2];
nbits[2] = bits[0];
nbits[3] = bits[1];
for (int j = 0; j < 4; j++) {
data[p++] = (byte) (nbits[j] & 0xff);
data[p++] = (byte) ((nbits[j] >> 8) & 0xff);
data[p++] = (byte) ((nbits[j] >> 16) & 0xff);
data[p++] = (byte) (nbits[j] >> 24);
}
break;
default:
throw new Exception ("Unrecognized type in MakeByteBlob: " + element_type);
}
idx += factor;
}
return data;
}
#if NET_4_0 || MONODROID
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
#if STATIC
return base.MakeExpression (ctx);
#else
var initializers = new SLE.Expression [array_data.Count];
for (var i = 0; i < initializers.Length; i++) {
if (array_data [i] == null)
initializers [i] = SLE.Expression.Default (array_element_type.GetMetaInfo ());
else
initializers [i] = array_data [i].MakeExpression (ctx);
}
return SLE.Expression.NewArrayInit (array_element_type.GetMetaInfo (), initializers);
#endif
}
#endif
#if STATIC
//
// Emits the initializers for the array
//
void EmitStaticInitializers (EmitContext ec, FieldExpr stackArray)
{
var m = ec.Module.PredefinedMembers.RuntimeHelpersInitializeArray.Resolve (loc);
if (m == null)
return;
//
// First, the static data
//
byte [] data = MakeByteBlob ();
var fb = ec.CurrentTypeDefinition.Module.MakeStaticData (data, loc);
if (stackArray == null) {
ec.Emit (OpCodes.Dup);
} else {
stackArray.Emit (ec);
}
ec.Emit (OpCodes.Ldtoken, fb);
ec.Emit (OpCodes.Call, m);
}
#endif
//
// Emits pieces of the array that can not be computed at compile
// time (variables and string locations).
//
// This always expect the top value on the stack to be the array
//
void EmitDynamicInitializers (EmitContext ec, bool emitConstants, FieldExpr stackArray)
{
int dims = bounds.Count;
var current_pos = new int [dims];
for (int i = 0; i < array_data.Count; i++){
Expression e = array_data [i];
var c = e as Constant;
// Constant can be initialized via StaticInitializer
if (c == null || (c != null && emitConstants && !c.IsDefaultInitializer (array_element_type))) {
var etype = e.Type;
if (stackArray != null) {
if (e.ContainsEmitWithAwait ()) {
e = e.EmitToField (ec);
}
stackArray.Emit (ec);
} else {
ec.Emit (OpCodes.Dup);
}
for (int idx = 0; idx < dims; idx++)
ec.EmitInt (current_pos [idx]);
//
// If we are dealing with a struct, get the
// address of it, so we can store it.
//
if (dims == 1 && etype.IsStruct) {
switch (etype.BuiltinType) {
case BuiltinTypeSpec.Type.Byte:
case BuiltinTypeSpec.Type.SByte:
case BuiltinTypeSpec.Type.Bool:
case BuiltinTypeSpec.Type.Short:
case BuiltinTypeSpec.Type.UShort:
case BuiltinTypeSpec.Type.Char:
case BuiltinTypeSpec.Type.Int:
case BuiltinTypeSpec.Type.UInt:
case BuiltinTypeSpec.Type.Long:
case BuiltinTypeSpec.Type.ULong:
case BuiltinTypeSpec.Type.Float:
case BuiltinTypeSpec.Type.Double:
break;
default:
ec.Emit (OpCodes.Ldelema, etype);
break;
}
}
e.Emit (ec);
ec.EmitArrayStore ((ArrayContainer) type);
}
//
// Advance counter
//
for (int j = dims - 1; j >= 0; j--){
current_pos [j]++;
if (current_pos [j] < bounds [j])
break;
current_pos [j] = 0;
}
}
}
public override void Emit (EmitContext ec)
{
if (first_emit != null) {
first_emit.Emit (ec);
first_emit_temp.Store (ec);
}
FieldExpr await_stack_field;
if (ec.HasSet (BuilderContext.Options.AsyncBody) && InitializersContainAwait ()) {
await_stack_field = ec.GetTemporaryField (type);
ec.EmitThis ();
} else {
await_stack_field = null;
}
EmitExpressionsList (ec, arguments);
ec.EmitArrayNew ((ArrayContainer) type);
if (initializers == null)
return;
if (await_stack_field != null)
await_stack_field.EmitAssignFromStack (ec);
#if STATIC
//
// Emit static initializer for arrays which contain more than 2 items and
// the static initializer will initialize at least 25% of array values or there
// is more than 10 items to be initialized
//
// NOTE: const_initializers_count does not contain default constant values.
//
if (const_initializers_count > 2 && (array_data.Count > 10 || const_initializers_count * 4 > (array_data.Count)) &&
(BuiltinTypeSpec.IsPrimitiveType (array_element_type) || array_element_type.IsEnum)) {
EmitStaticInitializers (ec, await_stack_field);
if (!only_constant_initializers)
EmitDynamicInitializers (ec, false, await_stack_field);
} else
#endif
{
EmitDynamicInitializers (ec, true, await_stack_field);
}
if (await_stack_field != null)
await_stack_field.Emit (ec);
if (first_emit_temp != null)
first_emit_temp.Release (ec);
}
public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType)
{
// no multi dimensional or jagged arrays
if (arguments.Count != 1 || array_element_type.IsArray) {
base.EncodeAttributeValue (rc, enc, targetType);
return;
}
// No array covariance, except for array -> object
if (type != targetType) {
if (targetType.BuiltinType != BuiltinTypeSpec.Type.Object) {
base.EncodeAttributeValue (rc, enc, targetType);
return;
}
if (enc.Encode (type) == AttributeEncoder.EncodedTypeProperties.DynamicType) {
Attribute.Error_AttributeArgumentIsDynamic (rc, loc);
return;
}
}
// Single dimensional array of 0 size
if (array_data == null) {
IntConstant ic = arguments[0] as IntConstant;
if (ic == null || !ic.IsDefaultValue) {
base.EncodeAttributeValue (rc, enc, targetType);
} else {
enc.Encode (0);
}
return;
}
enc.Encode (array_data.Count);
foreach (var element in array_data) {
element.EncodeAttributeValue (rc, enc, array_element_type);
}
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
ArrayCreation target = (ArrayCreation) t;
if (requested_base_type != null)
target.requested_base_type = (FullNamedExpression)requested_base_type.Clone (clonectx);
if (arguments != null){
target.arguments = new List<Expression> (arguments.Count);
foreach (Expression e in arguments)
target.arguments.Add (e.Clone (clonectx));
}
if (initializers != null)
target.initializers = (ArrayInitializer) initializers.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
//
// Represents an implicitly typed array epxression
//
class ImplicitlyTypedArrayCreation : ArrayCreation
{
sealed class InferenceContext : TypeInferenceContext
{
class ExpressionBoundInfo : BoundInfo
{
readonly Expression expr;
public ExpressionBoundInfo (Expression expr)
: base (expr.Type, BoundKind.Lower)
{
this.expr = expr;
}
public override bool Equals (BoundInfo other)
{
// We are using expression not type for conversion check
// no optimization based on types is possible
return false;
}
public override Expression GetTypeExpression ()
{
return expr;
}
}
public void AddExpression (Expression expr)
{
AddToBounds (new ExpressionBoundInfo (expr), 0);
}
}
InferenceContext best_type_inference;
public ImplicitlyTypedArrayCreation (ComposedTypeSpecifier rank, ArrayInitializer initializers, Location loc)
: base (null, rank, initializers, loc)
{
}
public ImplicitlyTypedArrayCreation (ArrayInitializer initializers, Location loc)
: base (null, initializers, loc)
{
}
protected override Expression DoResolve (ResolveContext ec)
{
if (type != null)
return this;
dimensions = rank.Dimension;
best_type_inference = new InferenceContext ();
if (!ResolveInitializers (ec))
return null;
best_type_inference.FixAllTypes (ec);
array_element_type = best_type_inference.InferredTypeArguments[0];
best_type_inference = null;
if (array_element_type == null ||
array_element_type == InternalType.NullLiteral || array_element_type == InternalType.MethodGroup || array_element_type == InternalType.AnonymousMethod ||
arguments.Count != rank.Dimension) {
ec.Report.Error (826, loc,
"The type of an implicitly typed array cannot be inferred from the initializer. Try specifying array type explicitly");
return null;
}
//
// At this point we found common base type for all initializer elements
// but we have to be sure that all static initializer elements are of
// same type
//
UnifyInitializerElement (ec);
type = ArrayContainer.MakeType (ec.Module, array_element_type, dimensions);
eclass = ExprClass.Value;
return this;
}
//
// Converts static initializer only
//
void UnifyInitializerElement (ResolveContext ec)
{
for (int i = 0; i < array_data.Count; ++i) {
Expression e = array_data[i];
if (e != null)
array_data [i] = Convert.ImplicitConversion (ec, e, array_element_type, Location.Null);
}
}
protected override Expression ResolveArrayElement (ResolveContext ec, Expression element)
{
element = element.Resolve (ec);
if (element != null)
best_type_inference.AddExpression (element);
return element;
}
}
sealed class CompilerGeneratedThis : This
{
public CompilerGeneratedThis (TypeSpec type, Location loc)
: base (loc)
{
this.type = type;
eclass = ExprClass.Variable;
}
protected override Expression DoResolve (ResolveContext ec)
{
return this;
}
public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
{
return null;
}
}
/// <summary>
/// Represents the `this' construct
/// </summary>
public class This : VariableReference
{
sealed class ThisVariable : ILocalVariable
{
public static readonly ILocalVariable Instance = new ThisVariable ();
public void Emit (EmitContext ec)
{
ec.EmitThis ();
}
public void EmitAssign (EmitContext ec)
{
throw new InvalidOperationException ();
}
public void EmitAddressOf (EmitContext ec)
{
ec.EmitThis ();
}
}
VariableInfo variable_info;
public This (Location loc)
{
this.loc = loc;
}
#region Properties
public override string Name {
get { return "this"; }
}
public override bool IsLockedByStatement {
get {
return false;
}
set {
}
}
public override bool IsRef {
get { return type.IsStruct; }
}
public override bool IsSideEffectFree {
get {
return true;
}
}
protected override ILocalVariable Variable {
get { return ThisVariable.Instance; }
}
public override VariableInfo VariableInfo {
get { return variable_info; }
}
public override bool IsFixed {
get { return false; }
}
#endregion
public void CheckStructThisDefiniteAssignment (ResolveContext rc)
{
//
// It's null for all cases when we don't need to check `this'
// definitive assignment
//
if (variable_info == null)
return;
if (rc.HasSet (ResolveContext.Options.OmitStructFlowAnalysis))
return;
if (!variable_info.IsAssigned (rc)) {
rc.Report.Error (188, loc,
"The `this' object cannot be used before all of its fields are assigned to");
}
}
protected virtual void Error_ThisNotAvailable (ResolveContext ec)
{
if (ec.IsStatic && !ec.HasSet (ResolveContext.Options.ConstantScope)) {
ec.Report.Error (26, loc, "Keyword `this' is not valid in a static property, static method, or static field initializer");
} else if (ec.CurrentAnonymousMethod != null) {
ec.Report.Error (1673, loc,
"Anonymous methods inside structs cannot access instance members of `this'. " +
"Consider copying `this' to a local variable outside the anonymous method and using the local instead");
} else {
ec.Report.Error (27, loc, "Keyword `this' is not available in the current context");
}
}
public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
{
if (ae == null)
return null;
AnonymousMethodStorey storey = ae.Storey;
while (storey != null) {
AnonymousMethodStorey temp = storey.Parent as AnonymousMethodStorey;
if (temp == null)
return storey.HoistedThis;
storey = temp;
}
return null;
}
public static bool IsThisAvailable (ResolveContext ec, bool ignoreAnonymous)
{
if (ec.IsStatic || ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.BaseInitializer | ResolveContext.Options.ConstantScope))
return false;
if (ignoreAnonymous || ec.CurrentAnonymousMethod == null)
return true;
if (ec.CurrentType.IsStruct && !(ec.CurrentAnonymousMethod is StateMachineInitializer))
return false;
return true;
}
public virtual void ResolveBase (ResolveContext ec)
{
eclass = ExprClass.Variable;
type = ec.CurrentType;
if (!IsThisAvailable (ec, false)) {
Error_ThisNotAvailable (ec);
return;
}
var block = ec.CurrentBlock;
if (block != null) {
if (block.ParametersBlock.TopBlock.ThisVariable != null)
variable_info = block.ParametersBlock.TopBlock.ThisVariable.VariableInfo;
AnonymousExpression am = ec.CurrentAnonymousMethod;
if (am != null && ec.IsVariableCapturingRequired) {
am.SetHasThisAccess ();
}
}
}
protected override Expression DoResolve (ResolveContext ec)
{
ResolveBase (ec);
CheckStructThisDefiniteAssignment (ec);
return this;
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
if (eclass == ExprClass.Unresolved)
ResolveBase (ec);
if (variable_info != null)
variable_info.SetAssigned (ec);
if (type.IsClass){
if (right_side == EmptyExpression.UnaryAddress)
ec.Report.Error (459, loc, "Cannot take the address of `this' because it is read-only");
else if (right_side == EmptyExpression.OutAccess)
ec.Report.Error (1605, loc, "Cannot pass `this' as a ref or out argument because it is read-only");
else
ec.Report.Error (1604, loc, "Cannot assign to `this' because it is read-only");
}
return this;
}
public override int GetHashCode()
{
throw new NotImplementedException ();
}
public override bool Equals (object obj)
{
This t = obj as This;
if (t == null)
return false;
return true;
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
// Nothing
}
public override void SetHasAddressTaken ()
{
// Nothing
}
public override void VerifyAssigned (ResolveContext rc)
{
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Represents the `__arglist' construct
/// </summary>
public class ArglistAccess : Expression
{
public ArglistAccess (Location loc)
{
this.loc = loc;
}
protected override void CloneTo (CloneContext clonectx, Expression target)
{
// nothing.
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
throw new NotSupportedException ("ET");
}
protected override Expression DoResolve (ResolveContext ec)
{
eclass = ExprClass.Variable;
type = ec.Module.PredefinedTypes.RuntimeArgumentHandle.Resolve ();
if (ec.HasSet (ResolveContext.Options.FieldInitializerScope) || !ec.CurrentBlock.ParametersBlock.Parameters.HasArglist) {
ec.Report.Error (190, loc,
"The __arglist construct is valid only within a variable argument method");
}
return this;
}
public override void Emit (EmitContext ec)
{
ec.Emit (OpCodes.Arglist);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Represents the `__arglist (....)' construct
/// </summary>
public class Arglist : Expression
{
Arguments arguments;
public Arglist (Location loc)
: this (null, loc)
{
}
public Arglist (Arguments args, Location l)
{
arguments = args;
loc = l;
}
public Arguments Arguments {
get {
return arguments;
}
}
public MetaType[] ArgumentTypes {
get {
if (arguments == null)
return MetaType.EmptyTypes;
var retval = new MetaType[arguments.Count];
for (int i = 0; i < retval.Length; i++)
retval[i] = arguments[i].Expr.Type.GetMetaInfo ();
return retval;
}
}
public override bool ContainsEmitWithAwait ()
{
throw new NotImplementedException ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
ec.Report.Error (1952, loc, "An expression tree cannot contain a method with variable arguments");
return null;
}
protected override Expression DoResolve (ResolveContext ec)
{
eclass = ExprClass.Variable;
type = InternalType.Arglist;
if (arguments != null) {
bool dynamic; // Can be ignored as there is always only 1 overload
arguments.Resolve (ec, out dynamic);
}
return this;
}
public override void Emit (EmitContext ec)
{
if (arguments != null)
arguments.Emit (ec);
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Arglist target = (Arglist) t;
if (arguments != null)
target.arguments = arguments.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public class RefValueExpr : ShimExpression
{
FullNamedExpression texpr;
public FullNamedExpression FullNamedExpression {
get { return texpr;}
}
public RefValueExpr (Expression expr, FullNamedExpression texpr, Location loc)
: base (expr)
{
this.texpr = texpr;
this.loc = loc;
}
public FullNamedExpression TypeExpression {
get {
return texpr;
}
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
protected override Expression DoResolve (ResolveContext rc)
{
expr = expr.Resolve (rc);
type = texpr.ResolveAsType (rc);
if (expr == null || type == null)
return null;
expr = Convert.ImplicitConversionRequired (rc, expr, rc.Module.PredefinedTypes.TypedReference.Resolve (), loc);
eclass = ExprClass.Value;
return this;
}
public override void Emit (EmitContext ec)
{
expr.Emit (ec);
ec.Emit (OpCodes.Refanyval, type);
ec.EmitLoadFromPtr (type);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public class RefTypeExpr : ShimExpression
{
public RefTypeExpr (Expression expr, Location loc)
: base (expr)
{
this.loc = loc;
}
protected override Expression DoResolve (ResolveContext rc)
{
expr = expr.Resolve (rc);
if (expr == null)
return null;
expr = Convert.ImplicitConversionRequired (rc, expr, rc.Module.PredefinedTypes.TypedReference.Resolve (), loc);
if (expr == null)
return null;
type = rc.BuiltinTypes.Type;
eclass = ExprClass.Value;
return this;
}
public override void Emit (EmitContext ec)
{
expr.Emit (ec);
ec.Emit (OpCodes.Refanytype);
var m = ec.Module.PredefinedMembers.TypeGetTypeFromHandle.Resolve (loc);
if (m != null)
ec.Emit (OpCodes.Call, m);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public class MakeRefExpr : ShimExpression
{
public MakeRefExpr (Expression expr, Location loc)
: base (expr)
{
this.loc = loc;
}
public override bool ContainsEmitWithAwait ()
{
throw new NotImplementedException ();
}
protected override Expression DoResolve (ResolveContext rc)
{
expr = expr.ResolveLValue (rc, EmptyExpression.LValueMemberAccess);
type = rc.Module.PredefinedTypes.TypedReference.Resolve ();
eclass = ExprClass.Value;
return this;
}
public override void Emit (EmitContext ec)
{
((IMemoryLocation) expr).AddressOf (ec, AddressOp.Load);
ec.Emit (OpCodes.Mkrefany, expr.Type);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Implements the typeof operator
/// </summary>
public class TypeOf : Expression {
FullNamedExpression QueriedType;
TypeSpec typearg;
public TypeOf (FullNamedExpression queried_type, Location l)
{
QueriedType = queried_type;
loc = l;
}
//
// Use this constructor for any compiler generated typeof expression
//
public TypeOf (TypeSpec type, Location loc)
{
this.typearg = type;
this.loc = loc;
}
#region Properties
public override bool IsSideEffectFree {
get {
return true;
}
}
public TypeSpec TypeArgument {
get {
return typearg;
}
}
public FullNamedExpression TypeExpression {
get {
return QueriedType;
}
}
#endregion
protected override void CloneTo (CloneContext clonectx, Expression t)
{
TypeOf target = (TypeOf) t;
if (QueriedType != null)
target.QueriedType = (FullNamedExpression) QueriedType.Clone (clonectx);
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (2);
args.Add (new Argument (this));
args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
return CreateExpressionFactoryCall (ec, "Constant", args);
}
protected override Expression DoResolve (ResolveContext ec)
{
if (eclass != ExprClass.Unresolved)
return this;
if (typearg == null) {
//
// Pointer types are allowed without explicit unsafe, they are just tokens
//
using (ec.Set (ResolveContext.Options.UnsafeScope)) {
typearg = QueriedType.ResolveAsType (ec);
}
if (typearg == null)
return null;
if (typearg.Kind == MemberKind.Void && !(QueriedType is TypeExpression)) {
ec.Report.Error (673, loc, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
} else if (typearg.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
ec.Report.Error (1962, QueriedType.Location,
"The typeof operator cannot be used on the dynamic type");
}
}
type = ec.BuiltinTypes.Type;
// Even though what is returned is a type object, it's treated as a value by the compiler.
// In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
eclass = ExprClass.Value;
return this;
}
static bool ContainsDynamicType (TypeSpec type)
{
if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
return true;
var element_container = type as ElementTypeSpec;
if (element_container != null)
return ContainsDynamicType (element_container.Element);
foreach (var t in type.TypeArguments) {
if (ContainsDynamicType (t)) {
return true;
}
}
return false;
}
public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType)
{
// Target type is not System.Type therefore must be object
// and we need to use different encoding sequence
if (targetType != type)
enc.Encode (type);
if (typearg is InflatedTypeSpec) {
var gt = typearg;
do {
if (InflatedTypeSpec.ContainsTypeParameter (gt)) {
rc.Module.Compiler.Report.Error (416, loc, "`{0}': an attribute argument cannot use type parameters",
typearg.GetSignatureForError ());
return;
}
gt = gt.DeclaringType;
} while (gt != null);
}
if (ContainsDynamicType (typearg)) {
Attribute.Error_AttributeArgumentIsDynamic (rc, loc);
return;
}
enc.EncodeTypeName (typearg);
}
public override void Emit (EmitContext ec)
{
ec.Emit (OpCodes.Ldtoken, typearg);
var m = ec.Module.PredefinedMembers.TypeGetTypeFromHandle.Resolve (loc);
if (m != null)
ec.Emit (OpCodes.Call, m);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
sealed class TypeOfMethod : TypeOfMember<MethodSpec>
{
public TypeOfMethod (MethodSpec method, Location loc)
: base (method, loc)
{
}
protected override Expression DoResolve (ResolveContext ec)
{
if (member.IsConstructor) {
type = ec.Module.PredefinedTypes.ConstructorInfo.Resolve ();
} else {
type = ec.Module.PredefinedTypes.MethodInfo.Resolve ();
}
if (type == null)
return null;
return base.DoResolve (ec);
}
public override void Emit (EmitContext ec)
{
ec.Emit (OpCodes.Ldtoken, member);
base.Emit (ec);
ec.Emit (OpCodes.Castclass, type);
}
protected override PredefinedMember<MethodSpec> GetTypeFromHandle (EmitContext ec)
{
return ec.Module.PredefinedMembers.MethodInfoGetMethodFromHandle;
}
protected override PredefinedMember<MethodSpec> GetTypeFromHandleGeneric (EmitContext ec)
{
return ec.Module.PredefinedMembers.MethodInfoGetMethodFromHandle2;
}
}
abstract class TypeOfMember<T> : Expression where T : MemberSpec
{
protected readonly T member;
protected TypeOfMember (T member, Location loc)
{
this.member = member;
this.loc = loc;
}
public override bool IsSideEffectFree {
get {
return true;
}
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (2);
args.Add (new Argument (this));
args.Add (new Argument (new TypeOf (type, loc)));
return CreateExpressionFactoryCall (ec, "Constant", args);
}
protected override Expression DoResolve (ResolveContext ec)
{
eclass = ExprClass.Value;
return this;
}
public override void Emit (EmitContext ec)
{
bool is_generic = member.DeclaringType.IsGenericOrParentIsGeneric;
PredefinedMember<MethodSpec> p;
if (is_generic) {
p = GetTypeFromHandleGeneric (ec);
ec.Emit (OpCodes.Ldtoken, member.DeclaringType);
} else {
p = GetTypeFromHandle (ec);
}
var mi = p.Resolve (loc);
if (mi != null)
ec.Emit (OpCodes.Call, mi);
}
protected abstract PredefinedMember<MethodSpec> GetTypeFromHandle (EmitContext ec);
protected abstract PredefinedMember<MethodSpec> GetTypeFromHandleGeneric (EmitContext ec);
}
sealed class TypeOfField : TypeOfMember<FieldSpec>
{
public TypeOfField (FieldSpec field, Location loc)
: base (field, loc)
{
}
protected override Expression DoResolve (ResolveContext ec)
{
type = ec.Module.PredefinedTypes.FieldInfo.Resolve ();
if (type == null)
return null;
return base.DoResolve (ec);
}
public override void Emit (EmitContext ec)
{
ec.Emit (OpCodes.Ldtoken, member);
base.Emit (ec);
}
protected override PredefinedMember<MethodSpec> GetTypeFromHandle (EmitContext ec)
{
return ec.Module.PredefinedMembers.FieldInfoGetFieldFromHandle;
}
protected override PredefinedMember<MethodSpec> GetTypeFromHandleGeneric (EmitContext ec)
{
return ec.Module.PredefinedMembers.FieldInfoGetFieldFromHandle2;
}
}
/// <summary>
/// Implements the sizeof expression
/// </summary>
public class SizeOf : Expression {
readonly Expression texpr;
TypeSpec type_queried;
public SizeOf (Expression queried_type, Location l)
{
this.texpr = queried_type;
loc = l;
}
public override bool IsSideEffectFree {
get {
return true;
}
}
public Expression TypeExpression {
get {
return texpr;
}
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Error_PointerInsideExpressionTree (ec);
return null;
}
protected override Expression DoResolve (ResolveContext ec)
{
type_queried = texpr.ResolveAsType (ec);
if (type_queried == null)
return null;
if (type_queried.IsEnum)
type_queried = EnumSpec.GetUnderlyingType (type_queried);
int size_of = BuiltinTypeSpec.GetSize (type_queried);
if (size_of > 0) {
return new IntConstant (ec.BuiltinTypes, size_of, loc);
}
if (!TypeManager.VerifyUnmanaged (ec.Module, type_queried, loc)){
return null;
}
if (!ec.IsUnsafe) {
ec.Report.Error (233, loc,
"`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)",
TypeManager.CSharpName (type_queried));
}
type = ec.BuiltinTypes.Int;
eclass = ExprClass.Value;
return this;
}
public override void Emit (EmitContext ec)
{
ec.Emit (OpCodes.Sizeof, type_queried);
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Implements the qualified-alias-member (::) expression.
/// </summary>
public class QualifiedAliasMember : MemberAccess
{
public readonly string alias;
public static readonly string GlobalAlias = "global";
public QualifiedAliasMember (string alias, string identifier, Location l)
: base (null, identifier, l)
{
this.alias = alias;
}
public QualifiedAliasMember (string alias, string identifier, TypeArguments targs, Location l)
: base (null, identifier, targs, l)
{
this.alias = alias;
}
public QualifiedAliasMember (string alias, string identifier, int arity, Location l)
: base (null, identifier, arity, l)
{
this.alias = alias;
}
public string Alias {
get {
return alias;
}
}
public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext ec)
{
if (alias == GlobalAlias) {
expr = ec.Module.GlobalRootNamespace;
return base.ResolveAsTypeOrNamespace (ec);
}
int errors = ec.Module.Compiler.Report.Errors;
expr = ec.LookupNamespaceAlias (alias);
if (expr == null) {
if (errors == ec.Module.Compiler.Report.Errors)
ec.Module.Compiler.Report.Error (432, loc, "Alias `{0}' not found", alias);
return null;
}
return base.ResolveAsTypeOrNamespace (ec);
}
protected override Expression DoResolve (ResolveContext ec)
{
return ResolveAsTypeOrNamespace (ec);
}
public override string GetSignatureForError ()
{
string name = Name;
if (targs != null) {
name = Name + "<" + targs.GetSignatureForError () + ">";
}
return alias + "::" + name;
}
public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions)
{
if ((restrictions & MemberLookupRestrictions.InvocableOnly) != 0) {
rc.Module.Compiler.Report.Error (687, loc,
"The namespace alias qualifier `::' cannot be used to invoke a method. Consider using `.' instead",
GetSignatureForError ());
return null;
}
return DoResolve (rc);
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
// Nothing
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Implements the member access expression
/// </summary>
public class MemberAccess : ATypeNameExpression
{
protected Expression expr;
#if FULL_AST
public Location DotLocation {
get;
set;
}
#endif
public MemberAccess (Expression expr, string id)
: base (id, expr.Location)
{
this.expr = expr;
}
public MemberAccess (Expression expr, string identifier, Location loc)
: base (identifier, loc)
{
this.expr = expr;
}
public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
: base (identifier, args, loc)
{
this.expr = expr;
}
public MemberAccess (Expression expr, string identifier, int arity, Location loc)
: base (identifier, arity, loc)
{
this.expr = expr;
}
public Expression LeftExpression {
get {
return expr;
}
}
protected override Expression DoResolve (ResolveContext rc)
{
var e = DoResolveName (rc, null);
if (!rc.OmitStructFlowAnalysis) {
var fe = e as FieldExpr;
if (fe != null) {
fe.VerifyAssignedStructField (rc, null);
}
}
return e;
}
public override Expression DoResolveLValue (ResolveContext rc, Expression rhs)
{
var e = DoResolveName (rc, rhs);
if (!rc.OmitStructFlowAnalysis) {
var fe = e as FieldExpr;
if (fe != null && fe.InstanceExpression is FieldExpr) {
fe = (FieldExpr) fe.InstanceExpression;
fe.VerifyAssignedStructField (rc, rhs);
}
}
return e;
}
Expression DoResolveName (ResolveContext rc, Expression right_side)
{
Expression e = LookupNameExpression (rc, right_side == null ? MemberLookupRestrictions.ReadAccess : MemberLookupRestrictions.None);
if (e == null)
return null;
if (right_side != null) {
if (e is TypeExpr) {
e.Error_UnexpectedKind (rc, ResolveFlags.VariableOrValue, loc);
return null;
}
e = e.ResolveLValue (rc, right_side);
} else {
e = e.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.Type);
}
return e;
}
protected virtual void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type)
{
if (type == InternalType.NullLiteral && rc.IsRuntimeBinder)
rc.Report.Error (Report.RuntimeErrorId, loc, "Cannot perform member binding on `null' value");
else
expr.Error_OperatorCannotBeApplied (rc, loc, ".", type);
}
public static bool IsValidDotExpression (TypeSpec type)
{
const MemberKind dot_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.Delegate | MemberKind.Enum |
MemberKind.Interface | MemberKind.TypeParameter | MemberKind.ArrayType;
return (type.Kind & dot_kinds) != 0 || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
}
public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions)
{
var sn = expr as SimpleName;
const ResolveFlags flags = ResolveFlags.VariableOrValue | ResolveFlags.Type;
//
// Resolve the expression with flow analysis turned off, we'll do the definite
// assignment checks later. This is because we don't know yet what the expression
// will resolve to - it may resolve to a FieldExpr and in this case we must do the
// definite assignment check on the actual field and not on the whole struct.
//
using (rc.Set (ResolveContext.Options.OmitStructFlowAnalysis)) {
if (sn != null) {
expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity);
//
// Resolve expression which does have type set as we need expression type
// with disable flow analysis as we don't know whether left side expression
// is used as variable or type
//
if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess) {
using (rc.With (ResolveContext.Options.DoFlowAnalysis, false)) {
expr = expr.Resolve (rc);
}
} else if (expr is TypeParameterExpr) {
expr.Error_UnexpectedKind (rc, flags, sn.Location);
expr = null;
}
} else {
expr = expr.Resolve (rc, flags);
}
}
if (expr == null)
return null;
Namespace ns = expr as Namespace;
if (ns != null) {
var retval = ns.LookupTypeOrNamespace (rc, Name, Arity, LookupMode.Normal, loc);
if (retval == null) {
ns.Error_NamespaceDoesNotExist (rc, Name, Arity, loc);
return null;
}
if (HasTypeArguments)
return new GenericTypeExpr (retval.Type, targs, loc);
return retval;
}
MemberExpr me;
TypeSpec expr_type = expr.Type;
if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
me = expr as MemberExpr;
if (me != null)
me.ResolveInstanceExpression (rc, null);
//
// Run defined assigned checks on expressions resolved with
// disabled flow-analysis
//
if (sn != null) {
var vr = expr as VariableReference;
if (vr != null)
vr.VerifyAssigned (rc);
}
Arguments args = new Arguments (1);
args.Add (new Argument (expr));
return new DynamicMemberBinder (Name, args, loc);
}
if (!IsValidDotExpression (expr_type)) {
Error_OperatorCannotBeApplied (rc, expr_type);
return null;
}
var lookup_arity = Arity;
bool errorMode = false;
Expression member_lookup;
while (true) {
member_lookup = MemberLookup (rc, errorMode, expr_type, Name, lookup_arity, restrictions, loc);
if (member_lookup == null) {
//
// Try to look for extension method when member lookup failed
//
if (MethodGroupExpr.IsExtensionMethodArgument (expr)) {
var methods = rc.LookupExtensionMethod (expr_type, Name, lookup_arity);
if (methods != null) {
var emg = new ExtensionMethodGroupExpr (methods, expr, loc);
if (HasTypeArguments) {
if (!targs.Resolve (rc))
return null;
emg.SetTypeArguments (rc, targs);
}
//
// Run defined assigned checks on expressions resolved with
// disabled flow-analysis
//
if (sn != null && !errorMode) {
var vr = expr as VariableReference;
if (vr != null)
vr.VerifyAssigned (rc);
}
// TODO: it should really skip the checks bellow
return emg.Resolve (rc);
}
}
}
if (errorMode) {
if (member_lookup == null) {
var dep = expr_type.GetMissingDependencies ();
if (dep != null) {
ImportedTypeDefinition.Error_MissingDependency (rc, dep, loc);
} else if (expr is TypeExpr) {
base.Error_TypeDoesNotContainDefinition (rc, expr_type, Name);
} else {
Error_TypeDoesNotContainDefinition (rc, expr_type, Name);
}
return null;
}
if (member_lookup is MethodGroupExpr) {
// Leave it to overload resolution to report correct error
} else if (!(member_lookup is TypeExpr)) {
// TODO: rc.SymbolRelatedToPreviousError
ErrorIsInaccesible (rc, member_lookup.GetSignatureForError (), loc);
}
break;
}
if (member_lookup != null)
break;
lookup_arity = 0;
restrictions &= ~MemberLookupRestrictions.InvocableOnly;
errorMode = true;
}
TypeExpr texpr = member_lookup as TypeExpr;
if (texpr != null) {
if (!(expr is TypeExpr)) {
me = expr as MemberExpr;
if (me == null || me.ProbeIdenticalTypeName (rc, expr, sn) == expr) {
rc.Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
Name, member_lookup.GetSignatureForError ());
return null;
}
}
if (!texpr.Type.IsAccessible (rc)) {
rc.Report.SymbolRelatedToPreviousError (member_lookup.Type);
ErrorIsInaccesible (rc, member_lookup.Type.GetSignatureForError (), loc);
return null;
}
if (HasTypeArguments) {
return new GenericTypeExpr (member_lookup.Type, targs, loc);
}
return member_lookup;
}
me = member_lookup as MemberExpr;
if (sn != null && me.IsStatic && (expr = me.ProbeIdenticalTypeName (rc, expr, sn)) != expr) {
sn = null;
}
me = me.ResolveMemberAccess (rc, expr, sn);
if (Arity > 0) {
if (!targs.Resolve (rc))
return null;
me.SetTypeArguments (rc, targs);
}
//
// Run defined assigned checks on expressions resolved with
// disabled flow-analysis
//
if (sn != null && !(me is FieldExpr && TypeSpec.IsValueType (expr_type))) {
var vr = expr as VariableReference;
if (vr != null)
vr.VerifyAssigned (rc);
}
return me;
}
public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext rc)
{
FullNamedExpression fexpr = expr as FullNamedExpression;
if (fexpr == null) {
expr.ResolveAsType (rc);
return null;
}
FullNamedExpression expr_resolved = fexpr.ResolveAsTypeOrNamespace (rc);
if (expr_resolved == null)
return null;
Namespace ns = expr_resolved as Namespace;
if (ns != null) {
FullNamedExpression retval = ns.LookupTypeOrNamespace (rc, Name, Arity, LookupMode.Normal, loc);
if (retval == null) {
ns.Error_NamespaceDoesNotExist (rc, Name, Arity, loc);
} else if (HasTypeArguments) {
retval = new GenericTypeExpr (retval.Type, targs, loc);
if (retval.ResolveAsType (rc) == null)
return null;
}
return retval;
}
var tnew_expr = expr_resolved.ResolveAsType (rc);
if (tnew_expr == null)
return null;
TypeSpec expr_type = tnew_expr;
if (TypeManager.IsGenericParameter (expr_type)) {
rc.Module.Compiler.Report.Error (704, loc, "A nested type cannot be specified through a type parameter `{0}'",
tnew_expr.GetSignatureForError ());
return null;
}
var qam = this as QualifiedAliasMember;
if (qam != null) {
rc.Module.Compiler.Report.Error (431, loc,
"Alias `{0}' cannot be used with `::' since it denotes a type. Consider replacing `::' with `.'",
qam.Alias);
}
TypeSpec nested = null;
while (expr_type != null) {
nested = MemberCache.FindNestedType (expr_type, Name, Arity);
if (nested == null) {
if (expr_type == tnew_expr) {
Error_IdentifierNotFound (rc, expr_type, Name);
return null;
}
expr_type = tnew_expr;
nested = MemberCache.FindNestedType (expr_type, Name, Arity);
ErrorIsInaccesible (rc, nested.GetSignatureForError (), loc);
break;
}
if (nested.IsAccessible (rc))
break;
//
// Keep looking after inaccessible candidate but only if
// we are not in same context as the definition itself
//
if (expr_type.MemberDefinition == rc.CurrentMemberDefinition)
break;
expr_type = expr_type.BaseType;
}
TypeExpr texpr;
if (Arity > 0) {
if (HasTypeArguments) {
texpr = new GenericTypeExpr (nested, targs, loc);
} else {
texpr = new GenericOpenTypeExpr (nested, loc);
}
} else {
texpr = new TypeExpression (nested, loc);
}
if (texpr.ResolveAsType (rc) == null)
return null;
return texpr;
}
protected virtual void Error_IdentifierNotFound (IMemberContext rc, TypeSpec expr_type, string identifier)
{
var nested = MemberCache.FindNestedType (expr_type, Name, -System.Math.Max (1, Arity));
if (nested != null) {
Error_TypeArgumentsCannotBeUsed (rc, nested, Arity, expr.Location);
return;
}
var any_other_member = MemberLookup (rc, false, expr_type, Name, 0, MemberLookupRestrictions.None, loc);
if (any_other_member != null) {
any_other_member.Error_UnexpectedKind (rc, any_other_member, "type", any_other_member.ExprClassName, loc);
return;
}
rc.Module.Compiler.Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
Name, expr_type.GetSignatureForError ());
}
protected override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name)
{
if (ec.Module.Compiler.Settings.Version > LanguageVersion.ISO_2 && !ec.IsRuntimeBinder && MethodGroupExpr.IsExtensionMethodArgument (expr)) {
ec.Report.SymbolRelatedToPreviousError (type);
ec.Report.Error (1061, loc,
"Type `{0}' does not contain a definition for `{1}' and no extension method `{1}' of type `{0}' could be found (are you missing a using directive or an assembly reference?)",
type.GetSignatureForError (), name);
return;
}
base.Error_TypeDoesNotContainDefinition (ec, type, name);
}
public override string GetSignatureForError ()
{
return expr.GetSignatureForError () + "." + base.GetSignatureForError ();
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
MemberAccess target = (MemberAccess) t;
target.expr = expr.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Implements checked expressions
/// </summary>
public class CheckedExpr : Expression {
public Expression Expr;
public CheckedExpr (Expression e, Location l)
{
Expr = e;
loc = l;
}
public override bool ContainsEmitWithAwait ()
{
return Expr.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
return Expr.CreateExpressionTree (ec);
}
protected override Expression DoResolve (ResolveContext ec)
{
using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
Expr = Expr.Resolve (ec);
if (Expr == null)
return null;
if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression || Expr is DefaultValueExpression)
return Expr;
eclass = Expr.eclass;
type = Expr.Type;
return this;
}
public override void Emit (EmitContext ec)
{
using (ec.With (EmitContext.Options.CheckedScope, true))
Expr.Emit (ec);
}
public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
{
using (ec.With (EmitContext.Options.CheckedScope, true))
Expr.EmitBranchable (ec, target, on_true);
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
using (ctx.With (BuilderContext.Options.CheckedScope, true)) {
return Expr.MakeExpression (ctx);
}
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
CheckedExpr target = (CheckedExpr) t;
target.Expr = Expr.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Implements the unchecked expression
/// </summary>
public class UnCheckedExpr : Expression {
public Expression Expr;
public UnCheckedExpr (Expression e, Location l)
{
Expr = e;
loc = l;
}
public override bool ContainsEmitWithAwait ()
{
return Expr.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
return Expr.CreateExpressionTree (ec);
}
protected override Expression DoResolve (ResolveContext ec)
{
using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
Expr = Expr.Resolve (ec);
if (Expr == null)
return null;
if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression || Expr is DefaultValueExpression)
return Expr;
eclass = Expr.eclass;
type = Expr.Type;
return this;
}
public override void Emit (EmitContext ec)
{
using (ec.With (EmitContext.Options.CheckedScope, false))
Expr.Emit (ec);
}
public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
{
using (ec.With (EmitContext.Options.CheckedScope, false))
Expr.EmitBranchable (ec, target, on_true);
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
UnCheckedExpr target = (UnCheckedExpr) t;
target.Expr = Expr.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// An Element Access expression.
///
/// During semantic analysis these are transformed into
/// IndexerAccess, ArrayAccess or a PointerArithmetic.
/// </summary>
public class ElementAccess : Expression
{
public Arguments Arguments;
public Expression Expr;
public ElementAccess (Expression e, Arguments args, Location loc)
{
Expr = e;
this.loc = loc;
this.Arguments = args;
}
public override bool ContainsEmitWithAwait ()
{
return Expr.ContainsEmitWithAwait () || Arguments.ContainsEmitWithAwait ();
}
//
// We perform some simple tests, and then to "split" the emit and store
// code we create an instance of a different class, and return that.
//
Expression CreateAccessExpression (ResolveContext ec)
{
if (type.IsArray)
return (new ArrayAccess (this, loc));
if (type.IsPointer)
return MakePointerAccess (ec, type);
FieldExpr fe = Expr as FieldExpr;
if (fe != null) {
var ff = fe.Spec as FixedFieldSpec;
if (ff != null) {
return MakePointerAccess (ec, ff.ElementType);
}
}
var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false);
if (indexers != null || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
return new IndexerExpr (indexers, type, this);
}
ec.Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
type.GetSignatureForError ());
return null;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = Arguments.CreateForExpressionTree (ec, Arguments,
Expr.CreateExpressionTree (ec));
return CreateExpressionFactoryCall (ec, "ArrayIndex", args);
}
Expression MakePointerAccess (ResolveContext ec, TypeSpec type)
{
if (Arguments.Count != 1){
ec.Report.Error (196, loc, "A pointer must be indexed by only one value");
return null;
}
if (Arguments [0] is NamedArgument)
Error_NamedArgument ((NamedArgument) Arguments[0], ec.Report);
Expression p = new PointerArithmetic (Binary.Operator.Addition, Expr, Arguments [0].Expr.Resolve (ec), type, loc);
return new Indirection (p, loc);
}
protected override Expression DoResolve (ResolveContext ec)
{
Expr = Expr.Resolve (ec);
if (Expr == null)
return null;
type = Expr.Type;
// TODO: Create 1 result for Resolve and ResolveLValue ?
var res = CreateAccessExpression (ec);
if (res == null)
return null;
return res.Resolve (ec);
}
public override Expression DoResolveLValue (ResolveContext ec, Expression rhs)
{
Expr = Expr.Resolve (ec);
if (Expr == null)
return null;
type = Expr.Type;
var res = CreateAccessExpression (ec);
if (res == null)
return null;
bool lvalue_instance = rhs != null && type.IsStruct && (Expr is Invocation || Expr is PropertyExpr);
if (lvalue_instance) {
Expr.Error_ValueAssignment (ec, EmptyExpression.LValueMemberAccess);
}
return res.ResolveLValue (ec, rhs);
}
public override void Emit (EmitContext ec)
{
throw new Exception ("Should never be reached");
}
public static void Error_NamedArgument (NamedArgument na, Report Report)
{
Report.Error (1742, na.Location, "An element access expression cannot use named argument");
}
public override string GetSignatureForError ()
{
return Expr.GetSignatureForError ();
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
ElementAccess target = (ElementAccess) t;
target.Expr = Expr.Clone (clonectx);
if (Arguments != null)
target.Arguments = Arguments.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// Implements array access
/// </summary>
public class ArrayAccess : Expression, IDynamicAssign, IMemoryLocation {
//
// Points to our "data" repository
//
ElementAccess ea;
LocalTemporary temp;
bool prepared;
bool? has_await_args;
public ArrayAccess (ElementAccess ea_data, Location l)
{
ea = ea_data;
loc = l;
}
public void AddressOf (EmitContext ec, AddressOp mode)
{
var ac = (ArrayContainer) ea.Expr.Type;
LoadInstanceAndArguments (ec, false, false);
if (ac.Element.IsGenericParameter && mode == AddressOp.Load)
ec.Emit (OpCodes.Readonly);
ec.EmitArrayAddress (ac);
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
return ea.CreateExpressionTree (ec);
}
public override bool ContainsEmitWithAwait ()
{
return ea.ContainsEmitWithAwait ();
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
return DoResolve (ec);
}
protected override Expression DoResolve (ResolveContext ec)
{
// dynamic is used per argument in ConvertExpressionToArrayIndex case
bool dynamic;
ea.Arguments.Resolve (ec, out dynamic);
var ac = ea.Expr.Type as ArrayContainer;
int rank = ea.Arguments.Count;
if (ac.Rank != rank) {
ec.Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
rank.ToString (), ac.Rank.ToString ());
return null;
}
type = ac.Element;
if (type.IsPointer && !ec.IsUnsafe) {
UnsafeError (ec, ea.Location);
}
foreach (Argument a in ea.Arguments) {
if (a is NamedArgument)
ElementAccess.Error_NamedArgument ((NamedArgument) a, ec.Report);
a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr);
}
eclass = ExprClass.Variable;
return this;
}
protected override void Error_NegativeArrayIndex (ResolveContext ec, Location loc)
{
ec.Report.Warning (251, 2, loc, "Indexing an array with a negative index (array indices always start at zero)");
}
//
// Load the array arguments into the stack.
//
void LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait)
{
if (prepareAwait) {
ea.Expr = ea.Expr.EmitToField (ec);
} else if (duplicateArguments) {
ea.Expr.Emit (ec);
ec.Emit (OpCodes.Dup);
var copy = new LocalTemporary (ea.Expr.Type);
copy.Store (ec);
ea.Expr = copy;
} else {
ea.Expr.Emit (ec);
}
var dup_args = ea.Arguments.Emit (ec, duplicateArguments, prepareAwait);
if (dup_args != null)
ea.Arguments = dup_args;
}
public void Emit (EmitContext ec, bool leave_copy)
{
var ac = ea.Expr.Type as ArrayContainer;
if (prepared) {
ec.EmitLoadFromPtr (type);
} else {
if (!has_await_args.HasValue && ec.HasSet (BuilderContext.Options.AsyncBody) && ea.Arguments.ContainsEmitWithAwait ()) {
LoadInstanceAndArguments (ec, false, true);
}
LoadInstanceAndArguments (ec, false, false);
ec.EmitArrayLoad (ac);
}
if (leave_copy) {
ec.Emit (OpCodes.Dup);
temp = new LocalTemporary (this.type);
temp.Store (ec);
}
}
public override void Emit (EmitContext ec)
{
Emit (ec, false);
}
public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
{
var ac = (ArrayContainer) ea.Expr.Type;
TypeSpec t = source.Type;
has_await_args = ec.HasSet (BuilderContext.Options.AsyncBody) && (ea.Arguments.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ());
//
// When we are dealing with a struct, get the address of it to avoid value copy
// Same cannot be done for reference type because array covariance and the
// check in ldelema requires to specify the type of array element stored at the index
//
if (t.IsStruct && ((isCompound && !(source is DynamicExpressionStatement)) || !BuiltinTypeSpec.IsPrimitiveType (t))) {
LoadInstanceAndArguments (ec, false, has_await_args.Value);
if (has_await_args.Value) {
if (source.ContainsEmitWithAwait ()) {
source = source.EmitToField (ec);
isCompound = false;
prepared = true;
}
LoadInstanceAndArguments (ec, isCompound, false);
} else {
prepared = true;
}
ec.EmitArrayAddress (ac);
if (isCompound) {
ec.Emit (OpCodes.Dup);
prepared = true;
}
} else {
LoadInstanceAndArguments (ec, isCompound, has_await_args.Value);
if (has_await_args.Value) {
if (source.ContainsEmitWithAwait ())
source = source.EmitToField (ec);
LoadInstanceAndArguments (ec, false, false);
}
}
source.Emit (ec);
if (isCompound) {
var lt = ea.Expr as LocalTemporary;
if (lt != null)
lt.Release (ec);
}
if (leave_copy) {
ec.Emit (OpCodes.Dup);
temp = new LocalTemporary (this.type);
temp.Store (ec);
}
if (prepared) {
ec.EmitStoreFromPtr (t);
} else {
ec.EmitArrayStore (ac);
}
if (temp != null) {
temp.Emit (ec);
temp.Release (ec);
}
}
public override Expression EmitToField (EmitContext ec)
{
//
// Have to be specialized for arrays to get access to
// underlying element. Instead of another result copy we
// need direct access to element
//
// Consider:
//
// CallRef (ref a[await Task.Factory.StartNew (() => 1)]);
//
ea.Expr = ea.Expr.EmitToField (ec);
return this;
}
public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source)
{
#if NET_4_0 || MONODROID
return SLE.Expression.ArrayAccess (ea.Expr.MakeExpression (ctx), MakeExpressionArguments (ctx));
#else
throw new NotImplementedException ();
#endif
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
return SLE.Expression.ArrayIndex (ea.Expr.MakeExpression (ctx), MakeExpressionArguments (ctx));
}
SLE.Expression[] MakeExpressionArguments (BuilderContext ctx)
{
using (ctx.With (BuilderContext.Options.CheckedScope, true)) {
return Arguments.MakeExpression (ea.Arguments, ctx);
}
}
}
//
// Indexer access expression
//
sealed class IndexerExpr : PropertyOrIndexerExpr<IndexerSpec>, OverloadResolver.IBaseMembersProvider
{
IList<MemberSpec> indexers;
Arguments arguments;
TypeSpec queried_type;
public IndexerExpr (IList<MemberSpec> indexers, TypeSpec queriedType, ElementAccess ea)
: base (ea.Location)
{
this.indexers = indexers;
this.queried_type = queriedType;
this.InstanceExpression = ea.Expr;
this.arguments = ea.Arguments;
}
#region Properties
protected override Arguments Arguments {
get {
return arguments;
}
set {
arguments = value;
}
}
protected override TypeSpec DeclaringType {
get {
return best_candidate.DeclaringType;
}
}
public override bool IsInstance {
get {
return true;
}
}
public override bool IsStatic {
get {
return false;
}
}
public override string KindName {
get { return "indexer"; }
}
public override string Name {
get {
return "this";
}
}
#endregion
public override bool ContainsEmitWithAwait ()
{
return base.ContainsEmitWithAwait () || arguments.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = Arguments.CreateForExpressionTree (ec, arguments,
InstanceExpression.CreateExpressionTree (ec),
new TypeOfMethod (Getter, loc));
return CreateExpressionFactoryCall (ec, "Call", args);
}
public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
{
LocalTemporary await_source_arg = null;
if (isCompound) {
emitting_compound_assignment = true;
if (source is DynamicExpressionStatement) {
Emit (ec, false);
} else {
source.Emit (ec);
}
emitting_compound_assignment = false;
if (has_await_arguments) {
await_source_arg = new LocalTemporary (Type);
await_source_arg.Store (ec);
arguments.Add (new Argument (await_source_arg));
if (leave_copy) {
temp = await_source_arg;
}
has_await_arguments = false;
} else {
arguments = null;
if (leave_copy) {
ec.Emit (OpCodes.Dup);
temp = new LocalTemporary (Type);
temp.Store (ec);
}
}
} else {
if (leave_copy) {
if (ec.HasSet (BuilderContext.Options.AsyncBody) && (arguments.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ())) {
source = source.EmitToField (ec);
} else {
temp = new LocalTemporary (Type);
source.Emit (ec);
temp.Store (ec);
source = temp;
}
}
arguments.Add (new Argument (source));
}
var call = new CallEmitter ();
call.InstanceExpression = InstanceExpression;
if (arguments == null)
call.InstanceExpressionOnStack = true;
call.Emit (ec, Setter, arguments, loc);
if (temp != null) {
temp.Emit (ec);
temp.Release (ec);
} else if (leave_copy) {
source.Emit (ec);
}
if (await_source_arg != null) {
await_source_arg.Release (ec);
}
}
public override string GetSignatureForError ()
{
return best_candidate.GetSignatureForError ();
}
public override SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source)
{
#if STATIC
throw new NotSupportedException ();
#else
var value = new[] { source.MakeExpression (ctx) };
var args = Arguments.MakeExpression (arguments, ctx).Concat (value);
#if NET_4_0 || MONODROID
return SLE.Expression.Block (
SLE.Expression.Call (InstanceExpression.MakeExpression (ctx), (MethodInfo) Setter.GetMetaInfo (), args),
value [0]);
#else
return args.First ();
#endif
#endif
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
#if STATIC
return base.MakeExpression (ctx);
#else
var args = Arguments.MakeExpression (arguments, ctx);
return SLE.Expression.Call (InstanceExpression.MakeExpression (ctx), (MethodInfo) Getter.GetMetaInfo (), args);
#endif
}
protected override Expression OverloadResolve (ResolveContext rc, Expression right_side)
{
if (best_candidate != null)
return this;
eclass = ExprClass.IndexerAccess;
bool dynamic;
arguments.Resolve (rc, out dynamic);
if (indexers == null && InstanceExpression.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
dynamic = true;
} else {
var res = new OverloadResolver (indexers, OverloadResolver.Restrictions.None, loc);
res.BaseMembersProvider = this;
res.InstanceQualifier = this;
// TODO: Do I need 2 argument sets?
best_candidate = res.ResolveMember<IndexerSpec> (rc, ref arguments);
if (best_candidate != null)
type = res.BestCandidateReturnType;
else if (!res.BestCandidateIsDynamic)
return null;
}
//
// It has dynamic arguments
//
if (dynamic) {
Arguments args = new Arguments (arguments.Count + 1);
if (IsBase) {
rc.Report.Error (1972, loc,
"The indexer base access cannot be dynamically dispatched. Consider casting the dynamic arguments or eliminating the base access");
} else {
args.Add (new Argument (InstanceExpression));
}
args.AddRange (arguments);
best_candidate = null;
return new DynamicIndexBinder (args, loc);
}
//
// Try to avoid resolving left expression again
//
if (right_side != null)
ResolveInstanceExpression (rc, right_side);
return this;
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
IndexerExpr target = (IndexerExpr) t;
if (arguments != null)
target.arguments = arguments.Clone (clonectx);
}
public override void SetTypeArguments (ResolveContext ec, TypeArguments ta)
{
Error_TypeArgumentsCannotBeUsed (ec, "indexer", GetSignatureForError (), loc);
}
#region IBaseMembersProvider Members
IList<MemberSpec> OverloadResolver.IBaseMembersProvider.GetBaseMembers (TypeSpec baseType)
{
return baseType == null ? null : MemberCache.FindMembers (baseType, MemberCache.IndexerNameAlias, false);
}
IParametersMember OverloadResolver.IBaseMembersProvider.GetOverrideMemberParameters (MemberSpec member)
{
if (queried_type == member.DeclaringType)
return null;
var filter = new MemberFilter (MemberCache.IndexerNameAlias, 0, MemberKind.Indexer, ((IndexerSpec) member).Parameters, null);
return MemberCache.FindMember (queried_type, filter, BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as IParametersMember;
}
MethodGroupExpr OverloadResolver.IBaseMembersProvider.LookupExtensionMethod (ResolveContext rc)
{
return null;
}
#endregion
}
//
// A base access expression
//
public class BaseThis : This
{
public BaseThis (Location loc)
: base (loc)
{
}
public BaseThis (TypeSpec type, Location loc)
: base (loc)
{
this.type = type;
eclass = ExprClass.Variable;
}
#region Properties
public override string Name {
get {
return "base";
}
}
#endregion
public override Expression CreateExpressionTree (ResolveContext ec)
{
ec.Report.Error (831, loc, "An expression tree may not contain a base access");
return base.CreateExpressionTree (ec);
}
public override void Emit (EmitContext ec)
{
base.Emit (ec);
var context_type = ec.CurrentType;
if (context_type.IsStruct) {
ec.Emit (OpCodes.Ldobj, context_type);
ec.Emit (OpCodes.Box, context_type);
}
}
protected override void Error_ThisNotAvailable (ResolveContext ec)
{
if (ec.IsStatic) {
ec.Report.Error (1511, loc, "Keyword `base' is not available in a static method");
} else {
ec.Report.Error (1512, loc, "Keyword `base' is not available in the current context");
}
}
public override void ResolveBase (ResolveContext ec)
{
base.ResolveBase (ec);
type = ec.CurrentType.BaseType;
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
/// <summary>
/// This class exists solely to pass the Type around and to be a dummy
/// that can be passed to the conversion functions (this is used by
/// foreach implementation to typecast the object return value from
/// get_Current into the proper type. All code has been generated and
/// we only care about the side effect conversions to be performed
///
/// This is also now used as a placeholder where a no-action expression
/// is needed (the `New' class).
/// </summary>
public class EmptyExpression : Expression
{
sealed class OutAccessExpression : EmptyExpression
{
public OutAccessExpression (TypeSpec t)
: base (t)
{
}
public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
{
rc.Report.Error (206, right_side.Location,
"A property, indexer or dynamic member access may not be passed as `ref' or `out' parameter");
return null;
}
}
public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression (InternalType.FakeInternalType);
public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression (InternalType.FakeInternalType);
public static readonly EmptyExpression UnaryAddress = new EmptyExpression (InternalType.FakeInternalType);
public static readonly EmptyExpression EventAddition = new EmptyExpression (InternalType.FakeInternalType);
public static readonly EmptyExpression EventSubtraction = new EmptyExpression (InternalType.FakeInternalType);
public static readonly EmptyExpression MissingValue = new EmptyExpression (InternalType.FakeInternalType);
public static readonly Expression Null = new EmptyExpression (InternalType.FakeInternalType);
public static readonly EmptyExpression OutAccess = new OutAccessExpression (InternalType.FakeInternalType);
public EmptyExpression (TypeSpec t)
{
type = t;
eclass = ExprClass.Value;
loc = Location.Null;
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
throw new NotSupportedException ("ET");
}
protected override Expression DoResolve (ResolveContext ec)
{
return this;
}
public override void Emit (EmitContext ec)
{
// nothing, as we only exist to not do anything.
}
public override void EmitSideEffect (EmitContext ec)
{
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
sealed class EmptyAwaitExpression : EmptyExpression
{
public EmptyAwaitExpression (TypeSpec type)
: base (type)
{
}
public override bool ContainsEmitWithAwait ()
{
return true;
}
}
//
// Empty statement expression
//
public sealed class EmptyExpressionStatement : ExpressionStatement
{
public static readonly EmptyExpressionStatement Instance = new EmptyExpressionStatement ();
private EmptyExpressionStatement ()
{
loc = Location.Null;
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
return null;
}
public override void EmitStatement (EmitContext ec)
{
// Do nothing
}
protected override Expression DoResolve (ResolveContext ec)
{
eclass = ExprClass.Value;
type = ec.BuiltinTypes.Object;
return this;
}
public override void Emit (EmitContext ec)
{
// Do nothing
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public class ErrorExpression : EmptyExpression
{
public static readonly ErrorExpression Instance = new ErrorExpression ();
public readonly int ErrorCode;
public readonly string Error;
private ErrorExpression ()
: base (InternalType.ErrorType)
{
}
ErrorExpression (int errorCode, Location location, string error)
: base (InternalType.ErrorType)
{
this.ErrorCode = errorCode;
base.loc = location;
this.Error = error;
}
public static ErrorExpression Create (int errorCode, Location location, string error)
{
return new ErrorExpression (errorCode, location, error);
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
return this;
}
public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
{
return this;
}
public override void Error_UnexpectedKind (ResolveContext ec, ResolveFlags flags, Location loc)
{
}
public override void Error_ValueCannotBeConverted (ResolveContext ec, Location loc, TypeSpec target, bool expl)
{
}
public override void Error_OperatorCannotBeApplied (ResolveContext rc, Location loc, string oper, TypeSpec t)
{
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public class UserCast : Expression {
MethodSpec method;
Expression source;
public UserCast (MethodSpec method, Expression source, Location l)
{
this.method = method;
this.source = source;
type = method.ReturnType;
loc = l;
}
public Expression Source {
get {
return source;
}
}
public override bool ContainsEmitWithAwait ()
{
return source.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (3);
args.Add (new Argument (source.CreateExpressionTree (ec)));
args.Add (new Argument (new TypeOf (type, loc)));
args.Add (new Argument (new TypeOfMethod (method, loc)));
return CreateExpressionFactoryCall (ec, "Convert", args);
}
protected override Expression DoResolve (ResolveContext ec)
{
ObsoleteAttribute oa = method.GetAttributeObsolete ();
if (oa != null)
AttributeTester.Report_ObsoleteMessage (oa, GetSignatureForError (), loc, ec.Report);
eclass = ExprClass.Value;
return this;
}
public override void Emit (EmitContext ec)
{
source.Emit (ec);
ec.Emit (OpCodes.Call, method);
}
public override string GetSignatureForError ()
{
return TypeManager.CSharpSignature (method);
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
#if STATIC
return base.MakeExpression (ctx);
#else
return SLE.Expression.Convert (source.MakeExpression (ctx), type.GetMetaInfo (), (MethodInfo) method.GetMetaInfo ());
#endif
}
}
//
// Holds additional type specifiers like ?, *, []
//
public class ComposedTypeSpecifier
{
public static readonly ComposedTypeSpecifier SingleDimension = new ComposedTypeSpecifier (1, Location.Null);
public readonly int Dimension;
public readonly Location Location;
public ComposedTypeSpecifier (int specifier, Location loc)
{
this.Dimension = specifier;
this.Location = loc;
}
#region Properties
public bool IsNullable {
get {
return Dimension == -1;
}
}
public bool IsPointer {
get {
return Dimension == -2;
}
}
public ComposedTypeSpecifier Next { get; set; }
#endregion
public static ComposedTypeSpecifier CreateArrayDimension (int dimension, Location loc)
{
return new ComposedTypeSpecifier (dimension, loc);
}
public static ComposedTypeSpecifier CreateNullable (Location loc)
{
return new ComposedTypeSpecifier (-1, loc);
}
public static ComposedTypeSpecifier CreatePointer (Location loc)
{
return new ComposedTypeSpecifier (-2, loc);
}
public string GetSignatureForError ()
{
string s =
IsPointer ? "*" :
IsNullable ? "?" :
ArrayContainer.GetPostfixSignature (Dimension);
return Next != null ? s + Next.GetSignatureForError () : s;
}
}
// <summary>
// This class is used to "construct" the type during a typecast
// operation. Since the Type.GetType class in .NET can parse
// the type specification, we just use this to construct the type
// one bit at a time.
// </summary>
public class ComposedCast : TypeExpr {
FullNamedExpression left;
ComposedTypeSpecifier spec;
public FullNamedExpression Left {
get { return this.left; }
}
public ComposedTypeSpecifier Spec {
get {
return this.spec;
}
}
public ComposedCast (FullNamedExpression left, ComposedTypeSpecifier spec)
{
if (spec == null)
throw new ArgumentNullException ("spec");
this.left = left;
this.spec = spec;
this.loc = spec.Location;
}
public override TypeSpec ResolveAsType (IMemberContext ec)
{
type = left.ResolveAsType (ec);
if (type == null)
return null;
eclass = ExprClass.Type;
var single_spec = spec;
if (single_spec.IsNullable) {
type = new Nullable.NullableType (type, loc).ResolveAsType (ec);
if (type == null)
return null;
single_spec = single_spec.Next;
} else if (single_spec.IsPointer) {
if (!TypeManager.VerifyUnmanaged (ec.Module, type, loc))
return null;
if (!ec.IsUnsafe) {
UnsafeError (ec.Module.Compiler.Report, loc);
}
do {
type = PointerContainer.MakeType (ec.Module, type);
single_spec = single_spec.Next;
} while (single_spec != null && single_spec.IsPointer);
}
if (single_spec != null && single_spec.Dimension > 0) {
if (type.IsSpecialRuntimeType) {
ec.Module.Compiler.Report.Error (611, loc, "Array elements cannot be of type `{0}'", type.GetSignatureForError ());
} else if (type.IsStatic) {
ec.Module.Compiler.Report.SymbolRelatedToPreviousError (type);
ec.Module.Compiler.Report.Error (719, loc, "Array elements cannot be of static type `{0}'",
type.GetSignatureForError ());
} else {
MakeArray (ec.Module, single_spec);
}
}
return type;
}
void MakeArray (ModuleContainer module, ComposedTypeSpecifier spec)
{
if (spec.Next != null)
MakeArray (module, spec.Next);
type = ArrayContainer.MakeType (module, type, spec.Dimension);
}
public override string GetSignatureForError ()
{
return left.GetSignatureForError () + spec.GetSignatureForError ();
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
class FixedBufferPtr : Expression
{
readonly Expression array;
public FixedBufferPtr (Expression array, TypeSpec array_type, Location l)
{
this.type = array_type;
this.array = array;
this.loc = l;
}
public override bool ContainsEmitWithAwait ()
{
throw new NotImplementedException ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Error_PointerInsideExpressionTree (ec);
return null;
}
public override void Emit(EmitContext ec)
{
array.Emit (ec);
}
protected override Expression DoResolve (ResolveContext ec)
{
type = PointerContainer.MakeType (ec.Module, type);
eclass = ExprClass.Value;
return this;
}
}
//
// This class is used to represent the address of an array, used
// only by the Fixed statement, this generates "&a [0]" construct
// for fixed (char *pa = a)
//
class ArrayPtr : FixedBufferPtr
{
public ArrayPtr (Expression array, TypeSpec array_type, Location l):
base (array, array_type, l)
{
}
public override void Emit (EmitContext ec)
{
base.Emit (ec);
ec.EmitInt (0);
ec.Emit (OpCodes.Ldelema, ((PointerContainer) type).Element);
}
}
//
// Encapsulates a conversion rules required for array indexes
//
public class ArrayIndexCast : TypeCast
{
public ArrayIndexCast (Expression expr, TypeSpec returnType)
: base (expr, returnType)
{
if (expr.Type == returnType) // int -> int
throw new ArgumentException ("unnecessary array index conversion");
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
using (ec.Set (ResolveContext.Options.CheckedScope)) {
return base.CreateExpressionTree (ec);
}
}
public override void Emit (EmitContext ec)
{
child.Emit (ec);
switch (child.Type.BuiltinType) {
case BuiltinTypeSpec.Type.UInt:
ec.Emit (OpCodes.Conv_U);
break;
case BuiltinTypeSpec.Type.Long:
ec.Emit (OpCodes.Conv_Ovf_I);
break;
case BuiltinTypeSpec.Type.ULong:
ec.Emit (OpCodes.Conv_Ovf_I_Un);
break;
default:
throw new InternalErrorException ("Cannot emit cast to unknown array element type", type);
}
}
}
//
// Implements the `stackalloc' keyword
//
public class StackAlloc : Expression {
TypeSpec otype;
Expression t;
Expression count;
public StackAlloc (Expression type, Expression count, Location l)
{
t = type;
this.count = count;
loc = l;
}
public Expression TypeExpression {
get {
return this.t;
}
}
public Expression CountExpression {
get {
return this.count;
}
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
throw new NotSupportedException ("ET");
}
protected override Expression DoResolve (ResolveContext ec)
{
count = count.Resolve (ec);
if (count == null)
return null;
if (count.Type.BuiltinType != BuiltinTypeSpec.Type.UInt){
count = Convert.ImplicitConversionRequired (ec, count, ec.BuiltinTypes.Int, loc);
if (count == null)
return null;
}
Constant c = count as Constant;
if (c != null && c.IsNegative) {
ec.Report.Error (247, loc, "Cannot use a negative size with stackalloc");
}
if (ec.HasAny (ResolveContext.Options.CatchScope | ResolveContext.Options.FinallyScope)) {
ec.Report.Error (255, loc, "Cannot use stackalloc in finally or catch");
}
otype = t.ResolveAsType (ec);
if (otype == null)
return null;
if (!TypeManager.VerifyUnmanaged (ec.Module, otype, loc))
return null;
type = PointerContainer.MakeType (ec.Module, otype);
eclass = ExprClass.Value;
return this;
}
public override void Emit (EmitContext ec)
{
int size = BuiltinTypeSpec.GetSize (otype);
count.Emit (ec);
if (size == 0)
ec.Emit (OpCodes.Sizeof, otype);
else
ec.EmitInt (size);
ec.Emit (OpCodes.Mul_Ovf_Un);
ec.Emit (OpCodes.Localloc);
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
StackAlloc target = (StackAlloc) t;
target.count = count.Clone (clonectx);
target.t = t.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
//
// An object initializer expression
//
public class ElementInitializer : Assign
{
public readonly string Name;
public ElementInitializer (string name, Expression initializer, Location loc)
: base (null, initializer, loc)
{
this.Name = name;
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
ElementInitializer target = (ElementInitializer) t;
target.source = source.Clone (clonectx);
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (2);
FieldExpr fe = target as FieldExpr;
if (fe != null)
args.Add (new Argument (fe.CreateTypeOfExpression ()));
else
args.Add (new Argument (((PropertyExpr) target).CreateSetterTypeOfExpression (ec)));
string mname;
Expression arg_expr;
var cinit = source as CollectionOrObjectInitializers;
if (cinit == null) {
mname = "Bind";
arg_expr = source.CreateExpressionTree (ec);
} else {
mname = cinit.IsEmpty || cinit.Initializers[0] is ElementInitializer ? "MemberBind" : "ListBind";
arg_expr = cinit.CreateExpressionTree (ec, !cinit.IsEmpty);
}
args.Add (new Argument (arg_expr));
return CreateExpressionFactoryCall (ec, mname, args);
}
protected override Expression DoResolve (ResolveContext ec)
{
if (source == null)
return EmptyExpressionStatement.Instance;
var t = ec.CurrentInitializerVariable.Type;
if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
Arguments args = new Arguments (1);
args.Add (new Argument (ec.CurrentInitializerVariable));
target = new DynamicMemberBinder (Name, args, loc);
} else {
var member = MemberLookup (ec, false, t, Name, 0, MemberLookupRestrictions.ExactArity, loc);
if (member == null) {
member = Expression.MemberLookup (ec, true, t, Name, 0, MemberLookupRestrictions.ExactArity, loc);
if (member != null) {
// TODO: ec.Report.SymbolRelatedToPreviousError (member);
ErrorIsInaccesible (ec, member.GetSignatureForError (), loc);
return null;
}
}
if (member == null) {
Error_TypeDoesNotContainDefinition (ec, loc, t, Name);
return null;
}
if (!(member is PropertyExpr || member is FieldExpr)) {
ec.Report.Error (1913, loc,
"Member `{0}' cannot be initialized. An object initializer may only be used for fields, or properties",
member.GetSignatureForError ());
return null;
}
var me = member as MemberExpr;
if (me.IsStatic) {
ec.Report.Error (1914, loc,
"Static field or property `{0}' cannot be assigned in an object initializer",
me.GetSignatureForError ());
}
target = me;
me.InstanceExpression = ec.CurrentInitializerVariable;
}
if (source is CollectionOrObjectInitializers) {
Expression previous = ec.CurrentInitializerVariable;
ec.CurrentInitializerVariable = target;
source = source.Resolve (ec);
ec.CurrentInitializerVariable = previous;
if (source == null)
return null;
eclass = source.eclass;
type = source.Type;
return this;
}
return base.DoResolve (ec);
}
public override void EmitStatement (EmitContext ec)
{
if (source is CollectionOrObjectInitializers)
source.Emit (ec);
else
base.EmitStatement (ec);
}
}
//
// A collection initializer expression
//
class CollectionElementInitializer : Invocation
{
public class ElementInitializerArgument : Argument
{
public ElementInitializerArgument (Expression e)
: base (e)
{
}
}
sealed class AddMemberAccess : MemberAccess
{
public AddMemberAccess (Expression expr, Location loc)
: base (expr, "Add", loc)
{
}
protected override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name)
{
if (TypeManager.HasElementType (type))
return;
base.Error_TypeDoesNotContainDefinition (ec, type, name);
}
}
public CollectionElementInitializer (Expression argument)
: base (null, new Arguments (1))
{
base.arguments.Add (new ElementInitializerArgument (argument));
this.loc = argument.Location;
}
public CollectionElementInitializer (List<Expression> arguments, Location loc)
: base (null, new Arguments (arguments.Count))
{
foreach (Expression e in arguments)
base.arguments.Add (new ElementInitializerArgument (e));
this.loc = loc;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (2);
args.Add (new Argument (mg.CreateExpressionTree (ec)));
var expr_initializers = new ArrayInitializer (arguments.Count, loc);
foreach (Argument a in arguments)
expr_initializers.Add (a.CreateExpressionTree (ec));
args.Add (new Argument (new ArrayCreation (
CreateExpressionTypeExpression (ec, loc), expr_initializers, loc)));
return CreateExpressionFactoryCall (ec, "ElementInit", args);
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
CollectionElementInitializer target = (CollectionElementInitializer) t;
if (arguments != null)
target.arguments = arguments.Clone (clonectx);
}
protected override Expression DoResolve (ResolveContext ec)
{
base.expr = new AddMemberAccess (ec.CurrentInitializerVariable, loc);
return base.DoResolve (ec);
}
}
//
// A block of object or collection initializers
//
public class CollectionOrObjectInitializers : ExpressionStatement
{
IList<Expression> initializers;
bool is_collection_initialization;
public static readonly CollectionOrObjectInitializers Empty =
new CollectionOrObjectInitializers (Array.AsReadOnly (new Expression [0]), Location.Null);
public CollectionOrObjectInitializers (IList<Expression> initializers, Location loc)
{
this.initializers = initializers;
this.loc = loc;
}
public bool IsEmpty {
get {
return initializers.Count == 0;
}
}
public bool IsCollectionInitializer {
get {
return is_collection_initialization;
}
}
public IList<Expression> Initializers {
get {
return initializers;
}
}
protected override void CloneTo (CloneContext clonectx, Expression target)
{
CollectionOrObjectInitializers t = (CollectionOrObjectInitializers) target;
t.initializers = new List<Expression> (initializers.Count);
foreach (var e in initializers)
t.initializers.Add (e.Clone (clonectx));
}
public override bool ContainsEmitWithAwait ()
{
foreach (var e in initializers) {
if (e.ContainsEmitWithAwait ())
return true;
}
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
return CreateExpressionTree (ec, false);
}
public Expression CreateExpressionTree (ResolveContext ec, bool inferType)
{
var expr_initializers = new ArrayInitializer (initializers.Count, loc);
foreach (Expression e in initializers) {
Expression expr = e.CreateExpressionTree (ec);
if (expr != null)
expr_initializers.Add (expr);
}
if (inferType)
return new ImplicitlyTypedArrayCreation (expr_initializers, loc);
return new ArrayCreation (new TypeExpression (ec.Module.PredefinedTypes.MemberBinding.Resolve (), loc), expr_initializers, loc);
}
protected override Expression DoResolve (ResolveContext ec)
{
List<string> element_names = null;
for (int i = 0; i < initializers.Count; ++i) {
Expression initializer = initializers [i];
ElementInitializer element_initializer = initializer as ElementInitializer;
if (i == 0) {
if (element_initializer != null) {
element_names = new List<string> (initializers.Count);
element_names.Add (element_initializer.Name);
} else if (initializer is CompletingExpression){
initializer.Resolve (ec);
throw new InternalErrorException ("This line should never be reached");
} else {
var t = ec.CurrentInitializerVariable.Type;
// LAMESPEC: The collection must implement IEnumerable only, no dynamic support
if (!t.ImplementsInterface (ec.BuiltinTypes.IEnumerable, false) && t.BuiltinType != BuiltinTypeSpec.Type.Dynamic) {
ec.Report.Error (1922, loc, "A field or property `{0}' cannot be initialized with a collection " +
"object initializer because type `{1}' does not implement `{2}' interface",
ec.CurrentInitializerVariable.GetSignatureForError (),
TypeManager.CSharpName (ec.CurrentInitializerVariable.Type),
TypeManager.CSharpName (ec.BuiltinTypes.IEnumerable));
return null;
}
is_collection_initialization = true;
}
} else {
if (is_collection_initialization != (element_initializer == null)) {
ec.Report.Error (747, initializer.Location, "Inconsistent `{0}' member declaration",
is_collection_initialization ? "collection initializer" : "object initializer");
continue;
}
if (!is_collection_initialization) {
if (element_names.Contains (element_initializer.Name)) {
ec.Report.Error (1912, element_initializer.Location,
"An object initializer includes more than one member `{0}' initialization",
element_initializer.Name);
} else {
element_names.Add (element_initializer.Name);
}
}
}
Expression e = initializer.Resolve (ec);
if (e == EmptyExpressionStatement.Instance)
initializers.RemoveAt (i--);
else
initializers [i] = e;
}
type = ec.CurrentInitializerVariable.Type;
if (is_collection_initialization) {
if (TypeManager.HasElementType (type)) {
ec.Report.Error (1925, loc, "Cannot initialize object of type `{0}' with a collection initializer",
TypeManager.CSharpName (type));
}
}
eclass = ExprClass.Variable;
return this;
}
public override void Emit (EmitContext ec)
{
EmitStatement (ec);
}
public override void EmitStatement (EmitContext ec)
{
foreach (ExpressionStatement e in initializers) {
// TODO: need location region
ec.Mark (e.Location);
e.EmitStatement (ec);
}
}
}
//
// New expression with element/object initializers
//
public class NewInitialize : New
{
//
// This class serves as a proxy for variable initializer target instances.
// A real variable is assigned later when we resolve left side of an
// assignment
//
sealed class InitializerTargetExpression : Expression, IMemoryLocation
{
NewInitialize new_instance;
public InitializerTargetExpression (NewInitialize newInstance)
{
this.type = newInstance.type;
this.loc = newInstance.loc;
this.eclass = newInstance.eclass;
this.new_instance = newInstance;
}
public override bool ContainsEmitWithAwait ()
{
return false;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
// Should not be reached
throw new NotSupportedException ("ET");
}
protected override Expression DoResolve (ResolveContext ec)
{
return this;
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
return this;
}
public override void Emit (EmitContext ec)
{
Expression e = (Expression) new_instance.instance;
e.Emit (ec);
}
public override Expression EmitToField (EmitContext ec)
{
return (Expression) new_instance.instance;
}
#region IMemoryLocation Members
public void AddressOf (EmitContext ec, AddressOp mode)
{
new_instance.instance.AddressOf (ec, mode);
}
#endregion
}
CollectionOrObjectInitializers initializers;
IMemoryLocation instance;
public NewInitialize (FullNamedExpression requested_type, Arguments arguments, CollectionOrObjectInitializers initializers, Location l)
: base (requested_type, arguments, l)
{
this.initializers = initializers;
}
public CollectionOrObjectInitializers Initializers {
get {
return initializers;
}
}
protected override void CloneTo (CloneContext clonectx, Expression t)
{
base.CloneTo (clonectx, t);
NewInitialize target = (NewInitialize) t;
target.initializers = (CollectionOrObjectInitializers) initializers.Clone (clonectx);
}
public override bool ContainsEmitWithAwait ()
{
return base.ContainsEmitWithAwait () || initializers.ContainsEmitWithAwait ();
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (2);
args.Add (new Argument (base.CreateExpressionTree (ec)));
if (!initializers.IsEmpty)
args.Add (new Argument (initializers.CreateExpressionTree (ec, initializers.IsCollectionInitializer)));
return CreateExpressionFactoryCall (ec,
initializers.IsCollectionInitializer ? "ListInit" : "MemberInit",
args);
}
protected override Expression DoResolve (ResolveContext ec)
{
Expression e = base.DoResolve (ec);
if (type == null)
return null;
Expression previous = ec.CurrentInitializerVariable;
ec.CurrentInitializerVariable = new InitializerTargetExpression (this);
initializers.Resolve (ec);
ec.CurrentInitializerVariable = previous;
return e;
}
public override bool Emit (EmitContext ec, IMemoryLocation target)
{
bool left_on_stack = base.Emit (ec, target);
if (initializers.IsEmpty)
return left_on_stack;
LocalTemporary temp = null;
instance = target as LocalTemporary;
if (instance == null) {
if (!left_on_stack) {
VariableReference vr = target as VariableReference;
// FIXME: This still does not work correctly for pre-set variables
if (vr != null && vr.IsRef)
target.AddressOf (ec, AddressOp.Load);
((Expression) target).Emit (ec);
left_on_stack = true;
}
if (ec.HasSet (BuilderContext.Options.AsyncBody) && initializers.ContainsEmitWithAwait ()) {
instance = new EmptyAwaitExpression (Type).EmitToField (ec) as IMemoryLocation;
} else {
temp = new LocalTemporary (type);
instance = temp;
}
}
if (left_on_stack && temp != null)
temp.Store (ec);
initializers.Emit (ec);
if (left_on_stack) {
if (temp != null) {
temp.Emit (ec);
temp.Release (ec);
} else {
((Expression) instance).Emit (ec);
}
}
return left_on_stack;
}
protected override IMemoryLocation EmitAddressOf (EmitContext ec, AddressOp Mode)
{
instance = base.EmitAddressOf (ec, Mode);
if (!initializers.IsEmpty)
initializers.Emit (ec);
return instance;
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public class NewAnonymousType : New
{
static readonly AnonymousTypeParameter[] EmptyParameters = new AnonymousTypeParameter[0];
List<AnonymousTypeParameter> parameters;
readonly TypeContainer parent;
AnonymousTypeClass anonymous_type;
public NewAnonymousType (List<AnonymousTypeParameter> parameters, TypeContainer parent, Location loc)
: base (null, null, loc)
{
this.parameters = parameters;
this.parent = parent;
}
public List<AnonymousTypeParameter> Parameters {
get {
return this.parameters;
}
}
protected override void CloneTo (CloneContext clonectx, Expression target)
{
if (parameters == null)
return;
NewAnonymousType t = (NewAnonymousType) target;
t.parameters = new List<AnonymousTypeParameter> (parameters.Count);
foreach (AnonymousTypeParameter atp in parameters)
t.parameters.Add ((AnonymousTypeParameter) atp.Clone (clonectx));
}
AnonymousTypeClass CreateAnonymousType (ResolveContext ec, IList<AnonymousTypeParameter> parameters)
{
AnonymousTypeClass type = parent.Module.GetAnonymousType (parameters);
if (type != null)
return type;
type = AnonymousTypeClass.Create (parent, parameters, loc);
if (type == null)
return null;
int errors = ec.Report.Errors;
type.CreateContainer ();
type.DefineContainer ();
type.Define ();
if ((ec.Report.Errors - errors) == 0) {
parent.Module.AddAnonymousType (type);
}
return type;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
if (parameters == null)
return base.CreateExpressionTree (ec);
var init = new ArrayInitializer (parameters.Count, loc);
foreach (var m in anonymous_type.Members) {
var p = m as Property;
if (p != null)
init.Add (new TypeOfMethod (MemberCache.GetMember (type, p.Get.Spec), loc));
}
var ctor_args = new ArrayInitializer (arguments.Count, loc);
foreach (Argument a in arguments)
ctor_args.Add (a.CreateExpressionTree (ec));
Arguments args = new Arguments (3);
args.Add (new Argument (new TypeOfMethod (method, loc)));
args.Add (new Argument (new ArrayCreation (CreateExpressionTypeExpression (ec, loc), ctor_args, loc)));
args.Add (new Argument (new ImplicitlyTypedArrayCreation (init, loc)));
return CreateExpressionFactoryCall (ec, "New", args);
}
protected override Expression DoResolve (ResolveContext ec)
{
if (ec.HasSet (ResolveContext.Options.ConstantScope)) {
ec.Report.Error (836, loc, "Anonymous types cannot be used in this expression");
return null;
}
if (parameters == null) {
anonymous_type = CreateAnonymousType (ec, EmptyParameters);
RequestedType = new TypeExpression (anonymous_type.Definition, loc);
return base.DoResolve (ec);
}
bool error = false;
arguments = new Arguments (parameters.Count);
var t_args = new TypeSpec [parameters.Count];
for (int i = 0; i < parameters.Count; ++i) {
Expression e = parameters [i].Resolve (ec);
if (e == null) {
error = true;
continue;
}
arguments.Add (new Argument (e));
t_args [i] = e.Type;
}
if (error)
return null;
anonymous_type = CreateAnonymousType (ec, parameters);
if (anonymous_type == null)
return null;
type = anonymous_type.Definition.MakeGenericType (ec.Module, t_args);
method = (MethodSpec) MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly);
eclass = ExprClass.Value;
return this;
}
public override void EmitStatement (EmitContext ec)
{
base.EmitStatement (ec);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
}
public class AnonymousTypeParameter : ShimExpression
{
public readonly string Name;
public AnonymousTypeParameter (Expression initializer, string name, Location loc)
: base (initializer)
{
this.Name = name;
this.loc = loc;
}
public AnonymousTypeParameter (Parameter parameter)
: base (new SimpleName (parameter.Name, parameter.Location))
{
this.Name = parameter.Name;
this.loc = parameter.Location;
}
public override bool Equals (object o)
{
AnonymousTypeParameter other = o as AnonymousTypeParameter;
return other != null && Name == other.Name;
}
public override int GetHashCode ()
{
return Name.GetHashCode ();
}
protected override Expression DoResolve (ResolveContext ec)
{
Expression e = expr.Resolve (ec);
if (e == null)
return null;
if (e.eclass == ExprClass.MethodGroup) {
Error_InvalidInitializer (ec, e.ExprClassName);
return null;
}
type = e.Type;
if (type.Kind == MemberKind.Void || type == InternalType.NullLiteral || type == InternalType.AnonymousMethod || type.IsPointer) {
Error_InvalidInitializer (ec, type.GetSignatureForError ());
return null;
}
return e;
}
protected virtual void Error_InvalidInitializer (ResolveContext ec, string initializer)
{
ec.Report.Error (828, loc, "An anonymous type property `{0}' cannot be initialized with `{1}'",
Name, initializer);
}
}
}