public override void Emit (EmitContext ec)
{
var call = new CallEmitter ();
- call.EmitPredefined (ec, oper, arguments);
+ call.EmitPredefined (ec, oper, arguments, loc);
+ }
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ arguments.FlowAnalysis (fc);
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
Expr.EmitSideEffect (ec);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (Oper == Operator.AddressOf) {
+ var vr = Expr as VariableReference;
+ if (vr != null && vr.VariableInfo != null)
+ fc.SetVariableAssigned (vr.VariableInfo);
+
+ return;
+ }
+
+ Expr.FlowAnalysis (fc);
+ }
+
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
EmitCode (ec, false);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
protected Expression expr;
protected TypeSpec probe_type_expr;
- public Probe (Expression expr, Expression probe_type, Location l)
+ protected Probe (Expression expr, Expression probe_type, Location l)
{
ProbeType = probe_type;
loc = l;
return this;
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
protected abstract string OperatorName { get; }
protected override void CloneTo (CloneContext clonectx, Expression t)
{
if (result)
ec.Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
- TypeManager.CSharpName (probe_type_expr));
+ probe_type_expr.GetSignatureForError ());
else
ec.Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
- TypeManager.CSharpName (probe_type_expr));
+ probe_type_expr.GetSignatureForError ());
return ReducedExpression.Create (new BoolConstant (ec.BuiltinTypes, result, loc), this);
}
//
if (Convert.ExplicitReferenceConversionExists (d, t))
return this;
+
+ //
+ // open generic type
+ //
+ if (d is InflatedTypeSpec && InflatedTypeSpec.ContainsTypeParameter (d))
+ return this;
} else {
- if (TypeManager.IsGenericParameter (t))
- return ResolveGenericParameter (ec, d, (TypeParameterSpec) t);
+ var tps = t as TypeParameterSpec;
+ if (tps != null)
+ return ResolveGenericParameter (ec, d, tps);
if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
ec.Report.Warning (1981, 3, loc,
}
} else {
if (Convert.ImplicitReferenceConversionExists (d, t)) {
+ var c = expr as Constant;
+ if (c != null)
+ return CreateConstantResult (ec, !c.IsNull);
+
//
// Do not optimize for imported type
//
- if (d.MemberDefinition.IsImported && d.BuiltinType != BuiltinTypeSpec.Type.None)
+ if (d.MemberDefinition.IsImported && d.BuiltinType != BuiltinTypeSpec.Type.None &&
+ d.MemberDefinition.DeclaringAssembly != t.MemberDefinition.DeclaringAssembly) {
return this;
+ }
//
// Turn is check into simple null check for implicitly convertible reference types
this).Resolve (ec);
}
- if (Convert.ExplicitReferenceConversionExists (d, t)) {
+ if (Convert.ExplicitReferenceConversionExists (d, t))
+ return this;
+
+ //
+ // open generic type
+ //
+ if ((d is InflatedTypeSpec || d.IsArray) && InflatedTypeSpec.ContainsTypeParameter (d))
return this;
- }
}
}
return CreateConstantResult (ec, false);
}
- if (TypeManager.IsGenericParameter (expr.Type)) {
- if (expr.Type == d && TypeSpec.IsValueType (t))
+ if (expr.Type.IsGenericParameter) {
+ if (expr.Type == d && TypeSpec.IsValueType (t) && TypeSpec.IsValueType (d))
return CreateConstantResult (ec, true);
expr = new BoxedCast (expr, d);
} else {
ec.Report.Error (77, loc,
"The `as' operator cannot be used with a non-nullable value type `{0}'",
- TypeManager.CSharpName (type));
+ type.GetSignatureForError ());
}
return null;
}
return this;
}
- ec.Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
- TypeManager.CSharpName (etype), TypeManager.CSharpName (type));
+ if (etype != InternalType.ErrorType) {
+ ec.Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
+ etype.GetSignatureForError (), type.GetSignatureForError ());
+ }
return null;
}
return null;
if (type.IsStatic) {
- ec.Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
+ ec.Report.Error (716, loc, "Cannot convert to static type `{0}'", type.GetSignatureForError ());
return null;
}
{
protected readonly TypeSpec left;
protected readonly TypeSpec right;
+ protected readonly TypeSpec left_unwrap;
+ protected readonly TypeSpec right_unwrap;
public readonly Operator OperatorsMask;
public TypeSpec ReturnType;
if ((op_mask & Operator.ValuesOnlyMask) != 0)
throw new InternalErrorException ("Only masked values can be used");
+ if ((op_mask & Operator.NullableMask) != 0) {
+ left_unwrap = Nullable.NullableInfo.GetUnderlyingType (ltype);
+ right_unwrap = Nullable.NullableInfo.GetUnderlyingType (rtype);
+ } else {
+ left_unwrap = ltype;
+ right_unwrap = rtype;
+ }
+
this.left = ltype;
this.right = rtype;
this.OperatorsMask = op_mask;
this.ReturnType = return_type;
}
- public virtual Expression ConvertResult (ResolveContext ec, Binary b)
+ public bool IsLifted {
+ get {
+ return (OperatorsMask & Operator.NullableMask) != 0;
+ }
+ }
+
+ public virtual Expression ConvertResult (ResolveContext rc, Binary b)
{
+ Constant c;
+
+ var left_expr = b.left;
+ var right_expr = b.right;
+
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);
+ if (IsLifted) {
+ if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
+ b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+ b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+ }
+
+ if (right_expr.IsNull) {
+ if ((b.oper & Operator.EqualityMask) != 0) {
+ if (!left_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (left_expr.Type))
+ return b.CreateLiftedValueTypeResult (rc, left_expr.Type);
+ } else if ((b.oper & Operator.BitwiseMask) != 0) {
+ if (left_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool)
+ return Nullable.LiftedNull.CreateFromExpression (rc, b);
+ } else {
+ b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+ b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+
+ if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
+ return Nullable.LiftedNull.CreateFromExpression (rc, b);
+
+ return b.CreateLiftedValueTypeResult (rc, left);
+ }
+ } else if (left_expr.IsNull) {
+ if ((b.oper & Operator.EqualityMask) != 0) {
+ if (!right_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (right_expr.Type))
+ return b.CreateLiftedValueTypeResult (rc, right_expr.Type);
+ } else if ((b.oper & Operator.BitwiseMask) != 0) {
+ if (right_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool)
+ return Nullable.LiftedNull.CreateFromExpression (rc, b);
+ } else {
+ b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+ b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+
+ if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
+ return Nullable.LiftedNull.CreateFromExpression (rc, b);
+
+ return b.CreateLiftedValueTypeResult (rc, right);
+ }
+ }
+ }
//
// 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);
+ if (left.BuiltinType == BuiltinTypeSpec.Type.Decimal) {
+ b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+ b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+
+ return b.ResolveUserOperator (rc, b.left, b.right);
+ }
- var c = b.right as Constant;
+ c = right_expr 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 (c.IsDefaultValue) {
+ //
+ // Optimizes
+ //
+ // (expr + 0) to expr
+ // (expr - 0) to expr
+ // (bool? | false) to bool?
+ //
+ if (b.oper == Operator.Addition || b.oper == Operator.Subtraction ||
+ (b.oper == Operator.BitwiseOr && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) {
+ b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+ return ReducedExpression.Create (b.left, b).Resolve (rc);
+ }
+
+ //
+ // Optimizes (value &/&& 0) to 0
+ //
+ if ((b.oper == Operator.BitwiseAnd || b.oper == Operator.LogicalAnd) && !IsLifted) {
+ Constant side_effect = new SideEffectConstant (c, b.left, c.Location);
+ return ReducedExpression.Create (side_effect, b);
+ }
+ } else {
+ //
+ // Optimizes (bool? & true) to bool?
+ //
+ if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) {
+ return ReducedExpression.Create (b.left, b).Resolve (rc);
+ }
+ }
+
if ((b.oper == Operator.Multiply || b.oper == Operator.Division) && c.IsOneInteger)
- return ReducedExpression.Create (b.left, b).Resolve (ec);
- return b;
+ return ReducedExpression.Create (b.left, b).Resolve (rc);
+
+ if ((b.oper & Operator.ShiftMask) != 0 && c is IntConstant) {
+ b.right = new IntConstant (rc.BuiltinTypes, ((IntConstant) c).Value & GetShiftMask (left_unwrap), b.right.Location);
+ }
}
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 (c.IsDefaultValue) {
+ //
+ // Optimizes
+ //
+ // (0 + expr) to expr
+ // (false | bool?) to bool?
+ //
+ if (b.oper == Operator.Addition ||
+ (b.oper == Operator.BitwiseOr && right_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) {
+ b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+ return ReducedExpression.Create (b.right, b).Resolve (rc);
+ }
+
+ //
+ // Optimizes (false && expr) to false
+ //
+ if (b.oper == Operator.LogicalAnd && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) {
+ // No rhs side-effects
+ Expression.Warning_UnreachableExpression (rc, b.right.StartLocation);
+ return ReducedExpression.Create (c, b);
+ }
+
+ //
+ // Optimizes (0 & value) to 0
+ //
+ if (b.oper == Operator.BitwiseAnd && !IsLifted) {
+ Constant side_effect = new SideEffectConstant (c, b.right, c.Location);
+ return ReducedExpression.Create (side_effect, b);
+ }
+ } else {
+ //
+ // Optimizes (true & bool?) to bool?
+ //
+ if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) {
+ return ReducedExpression.Create (b.right, b).Resolve (rc);
+ }
+
+ //
+ // Optimizes (true || expr) to true
+ //
+ if (b.oper == Operator.LogicalOr && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) {
+ // No rhs side-effects
+ Expression.Warning_UnreachableExpression (rc, b.right.StartLocation);
+ return ReducedExpression.Create (c, b);
+ }
+ }
+
if (b.oper == Operator.Multiply && c.IsOneInteger)
- return ReducedExpression.Create (b.right, b).Resolve (ec);
- return b;
+ return ReducedExpression.Create (b.right, b).Resolve (rc);
+ }
+
+ if (IsLifted) {
+ var lifted = new Nullable.LiftedBinaryOperator (b);
+
+ TypeSpec ltype, rtype;
+ if (b.left.Type.IsNullableType) {
+ lifted.UnwrapLeft = new Nullable.Unwrap (b.left);
+ ltype = left_unwrap;
+ } else {
+ ltype = left;
+ }
+
+ if (b.right.Type.IsNullableType) {
+ lifted.UnwrapRight = new Nullable.Unwrap (b.right);
+ rtype = right_unwrap;
+ } else {
+ rtype = right;
+ }
+
+ lifted.Left = b.left.IsNull ?
+ b.left :
+ Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? b.left, ltype, b.left.Location);
+
+ lifted.Right = b.right.IsNull ?
+ b.right :
+ Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? b.right, rtype, b.right.Location);
+
+ return lifted.Resolve (rc);
}
+ b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+ b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+
return b;
}
public PredefinedOperator ResolveBetterOperator (ResolveContext ec, PredefinedOperator best_operator)
{
+ if ((OperatorsMask & Operator.DecomposedMask) != 0)
+ return best_operator;
+
+ if ((best_operator.OperatorsMask & Operator.DecomposedMask) != 0)
+ return this;
+
int result = 0;
if (left != null && best_operator.left != null) {
- result = OverloadResolver.BetterTypeConversion (ec, best_operator.left, left);
+ result = OverloadResolver.BetterTypeConversion (ec, best_operator.left_unwrap, left_unwrap);
}
//
// 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);
+ result |= OverloadResolver.BetterTypeConversion (ec, best_operator.right_unwrap, right_unwrap);
}
if (result == 0 || result > 2)
}
}
- 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)).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;
LogicalMask = 1 << 10,
AdditionMask = 1 << 11,
SubtractionMask = 1 << 12,
- RelationalMask = 1 << 13
+ RelationalMask = 1 << 13,
+
+ DecomposedMask = 1 << 19,
+ NullableMask = 1 << 20,
}
- protected enum State
+ [Flags]
+ enum State : byte
{
None = 0,
Compound = 1 << 1,
- LeftNullLifted = 1 << 2,
- RightNullLifted = 1 << 3
}
readonly Operator oper;
- protected Expression left, right;
- protected State state;
- Expression enum_conversion;
+ Expression left, right;
+ State state;
+ ConvCast.Mode enum_conversion;
public Binary (Operator oper, Expression left, Expression right, bool isCompound)
: this (oper, left, right)
}
}
+ public override Location StartLocation {
+ get {
+ return left.StartLocation;
+ }
+ }
+
#endregion
/// <summary>
return;
string l, r;
- l = TypeManager.CSharpName (left.Type);
- r = TypeManager.CSharpName (right.Type);
+ l = left.Type.GetSignatureForError ();
+ r = right.Type.GetSignatureForError ();
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)
+ void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right)
{
Error_OperatorCannotBeApplied (ec, left, right, OperName (oper), loc);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ left.FlowAnalysis (fc);
+ right.FlowAnalysis (fc);
+ }
+
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
}
- public static void EmitOperatorOpcode (EmitContext ec, Operator oper, TypeSpec l)
+ public static void EmitOperatorOpcode (EmitContext ec, Operator oper, TypeSpec l, Expression right)
{
OpCode opcode;
break;
case Operator.RightShift:
+ if (!(right is IntConstant)) {
+ ec.EmitInt (GetShiftMask (l));
+ ec.Emit (OpCodes.And);
+ }
+
if (IsUnsigned (l))
opcode = OpCodes.Shr_Un;
else
break;
case Operator.LeftShift:
+ if (!(right is IntConstant)) {
+ ec.EmitInt (GetShiftMask (l));
+ ec.Emit (OpCodes.And);
+ }
+
opcode = OpCodes.Shl;
break;
ec.Emit (opcode);
}
+ static int GetShiftMask (TypeSpec type)
+ {
+ return type.BuiltinType == BuiltinTypeSpec.Type.Int || type.BuiltinType == BuiltinTypeSpec.Type.UInt ? 0x1f : 0x3f;
+ }
+
static bool IsUnsigned (TypeSpec t)
{
switch (t.BuiltinType) {
return t.BuiltinType == BuiltinTypeSpec.Type.Float || t.BuiltinType == BuiltinTypeSpec.Type.Double;
}
- Expression ResolveOperator (ResolveContext ec)
+ public Expression ResolveOperator (ResolveContext rc)
{
+ eclass = ExprClass.Value;
+
TypeSpec l = left.Type;
TypeSpec r = right.Type;
Expression expr;
//
// Handles predefined primitive types
//
- if (BuiltinTypeSpec.IsPrimitiveType (l) && BuiltinTypeSpec.IsPrimitiveType (r)) {
+ if ((BuiltinTypeSpec.IsPrimitiveType (l) || (l.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (l)))) &&
+ (BuiltinTypeSpec.IsPrimitiveType (r) || (r.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (r))))) {
if ((oper & Operator.ShiftMask) == 0) {
- if (l.BuiltinType != BuiltinTypeSpec.Type.Bool && !DoBinaryOperatorPromotion (ec))
+ if (!DoBinaryOperatorPromotion (rc))
return null;
- primitives_only = true;
+ primitives_only = BuiltinTypeSpec.IsPrimitiveType (l) && BuiltinTypeSpec.IsPrimitiveType (r);
}
} else {
// Pointers
if (l.IsPointer || r.IsPointer)
- return ResolveOperatorPointer (ec, l, r);
+ return ResolveOperatorPointer (rc, l, r);
+
+ // User operators
+ expr = ResolveUserOperator (rc, left, right);
+ if (expr != null)
+ return expr;
+
- // Enums
bool lenum = l.IsEnum;
bool renum = r.IsEnum;
- if (lenum || renum) {
- expr = ResolveOperatorEnum (ec, lenum, renum, l, r);
+ if ((oper & (Operator.ComparisonMask | Operator.BitwiseMask)) != 0) {
+ //
+ // Enumerations
+ //
+ if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) {
+ expr = ResolveSingleEnumOperators (rc, lenum, renum, l, r);
- if (expr != null)
- return expr;
- }
+ if (expr == null)
+ return null;
- // Delegates
- if ((oper == Operator.Addition || oper == Operator.Subtraction) && (l.IsDelegate || r.IsDelegate)) {
-
- expr = ResolveOperatorDelegate (ec, l, r);
+ if ((oper & Operator.BitwiseMask) != 0) {
+ expr = EmptyCast.Create (expr, type);
+ AddEnumResultCast (type);
- // TODO: Can this be ambiguous
- if (expr != null)
+ if (oper == Operator.BitwiseAnd && left.Type.IsEnum && right.Type.IsEnum) {
+ expr = OptimizeAndOperation (expr);
+ }
+ }
+
+ left = ConvertEnumOperandToUnderlyingType (rc, left);
+ right = ConvertEnumOperandToUnderlyingType (rc, right);
return expr;
- }
+ }
+ } else if ((oper == Operator.Addition || oper == Operator.Subtraction)) {
+ if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) {
+ //
+ // Enumerations
+ //
+ expr = ResolveEnumOperators (rc, lenum, renum, l, r);
- // User operators
- expr = ResolveUserOperator (ec, left, right);
- if (expr != null)
- return expr;
+ //
+ // We cannot break here there is also Enum + String possible match
+ // which is not ambiguous with predefined enum operators
+ //
+ if (expr != null) {
+ left = ConvertEnumOperandToUnderlyingType (rc, left);
+ right = ConvertEnumOperandToUnderlyingType (rc, right);
- // Predefined reference types equality
- if ((oper & Operator.EqualityMask) != 0) {
- expr = ResolveOperatorEquality (ec, l, r);
- if (expr != null)
- return expr;
+ return expr;
+ }
+ } else if (l.IsDelegate || r.IsDelegate) {
+ //
+ // Delegates
+ //
+ expr = ResolveOperatorDelegate (rc, l, r);
+
+ // TODO: Can this be ambiguous
+ if (expr != null)
+ return expr;
+ }
}
}
+
+ //
+ // Equality operators are more complicated
+ //
+ if ((oper & Operator.EqualityMask) != 0) {
+ return ResolveEquality (rc, l, r, primitives_only);
+ }
+
+ expr = ResolveOperatorPredefined (rc, rc.BuiltinTypes.OperatorsBinaryStandard, primitives_only);
+ if (expr != null)
+ return expr;
+
+ if (primitives_only)
+ return null;
+
+ //
+ // Lifted operators have lower priority
+ //
+ return ResolveOperatorPredefined (rc, rc.Module.OperatorsBinaryLifted, false);
+ }
- return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryStandard, primitives_only, null);
+ static bool IsEnumOrNullableEnum (TypeSpec type)
+ {
+ return type.IsEnum || (type.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (type).IsEnum);
}
+
// 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)
+ Constant EnumLiftUp (ResolveContext ec, Constant left, Constant right)
{
switch (oper) {
case Operator.BitwiseOr:
// FIXME: consider constants
+ var ltype = lcast != null ? lcast.UnderlyingType : rcast.UnderlyingType;
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));
+ ltype.GetSignatureForError ());
}
public static PredefinedOperator[] CreatePointerOperatorsTable (BuiltinTypes types)
public static PredefinedOperator[] CreateStandardOperatorsTable (BuiltinTypes types)
{
TypeSpec bool_type = types.Bool;
- return new PredefinedOperator[] {
- new PredefinedOperator (types.Int, Operator.ArithmeticMask | Operator.BitwiseMask),
+
+ return new [] {
+ new PredefinedOperator (types.Int, Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask),
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.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),
+ // Remaining string operators are in lifted tables
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)
+ new PredefinedOperator (types.UInt, types.Int, Operator.ShiftMask),
+ new PredefinedOperator (types.Long, types.Int, Operator.ShiftMask),
+ new PredefinedOperator (types.ULong, types.Int, Operator.ShiftMask)
};
- }
- public static PredefinedOperator[] CreateEqualityOperatorsTable (BuiltinTypes types)
+ }
+ public static PredefinedOperator[] CreateStandardLiftedOperatorsTable (ModuleContainer module)
{
- TypeSpec bool_type = types.Bool;
+ var nullable = module.PredefinedTypes.Nullable.TypeSpec;
+ if (nullable == null)
+ return new PredefinedOperator [0];
- return new PredefinedOperator[] {
- new PredefinedEqualityOperator (types.String, bool_type),
- new PredefinedEqualityOperator (types.Delegate, bool_type),
- new PredefinedOperator (bool_type, Operator.EqualityMask, bool_type)
- };
- }
+ var types = module.Compiler.BuiltinTypes;
+ var bool_type = types.Bool;
- //
- // Rules used during binary numeric promotion
- //
- static bool DoNumericPromotion (ResolveContext rc, ref Expression prim_expr, ref Expression second_expr, TypeSpec type)
- {
- Expression temp;
+ var nullable_bool = nullable.MakeGenericType (module, new[] { bool_type });
+ var nullable_int = nullable.MakeGenericType (module, new[] { types.Int });
+ var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt });
+ var nullable_long = nullable.MakeGenericType (module, new[] { types.Long });
+ var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong });
+ var nullable_float = nullable.MakeGenericType (module, new[] { types.Float });
+ var nullable_double = nullable.MakeGenericType (module, new[] { types.Double });
+ var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal });
- Constant c = prim_expr as Constant;
- if (c != null) {
- temp = c.ConvertImplicitly (type);
- if (temp != null) {
- prim_expr = temp;
- return true;
- }
- }
+ return new[] {
+ new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask),
+ new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask),
+ new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask),
+ new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask),
+ new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ArithmeticMask),
+ new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ArithmeticMask),
+ new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ArithmeticMask),
- 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;
+ new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+ new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+ new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+ new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+ new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+ new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+ new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+
+ new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.BitwiseMask, nullable_bool),
+
+ new PredefinedOperator (nullable_uint, nullable_int, Operator.NullableMask | Operator.ShiftMask),
+ new PredefinedOperator (nullable_long, nullable_int, Operator.NullableMask | Operator.ShiftMask),
+ new PredefinedOperator (nullable_ulong, nullable_int, Operator.NullableMask | Operator.ShiftMask),
- 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
+ // Not strictly lifted but need to be in second group otherwise expressions like
+ // int + null would resolve to +(object, string) instead of +(int?, int?)
//
- switch (type.BuiltinType) {
- case BuiltinTypeSpec.Type.Int:
- case BuiltinTypeSpec.Type.Long:
- case BuiltinTypeSpec.Type.Short:
- case BuiltinTypeSpec.Type.SByte:
- return false;
- }
- }
+ new PredefinedStringOperator (types.String, types.Object, Operator.AdditionMask, types.String),
+ new PredefinedStringOperator (types.Object, types.String, Operator.AdditionMask, types.String),
- temp = Convert.ImplicitNumericConversion (prim_expr, type);
- if (temp == null)
- return false;
+ };
+ }
- prim_expr = temp;
- return true;
+ public static PredefinedOperator[] CreateEqualityOperatorsTable (BuiltinTypes types)
+ {
+ TypeSpec bool_type = types.Bool;
+
+ return new[] {
+ new PredefinedEqualityOperator (types.String, bool_type),
+ new PredefinedEqualityOperator (types.Delegate, bool_type),
+ new PredefinedOperator (bool_type, Operator.EqualityMask, bool_type),
+ new PredefinedOperator (types.Int, Operator.EqualityMask, bool_type),
+ new PredefinedOperator (types.UInt, Operator.EqualityMask, bool_type),
+ new PredefinedOperator (types.Long, Operator.EqualityMask, bool_type),
+ new PredefinedOperator (types.ULong, Operator.EqualityMask, bool_type),
+ new PredefinedOperator (types.Float, Operator.EqualityMask, bool_type),
+ new PredefinedOperator (types.Double, Operator.EqualityMask, bool_type),
+ new PredefinedOperator (types.Decimal, Operator.EqualityMask, bool_type),
+ };
+ }
+
+ public static PredefinedOperator[] CreateEqualityLiftedOperatorsTable (ModuleContainer module)
+ {
+ var nullable = module.PredefinedTypes.Nullable.TypeSpec;
+
+ if (nullable == null)
+ return new PredefinedOperator [0];
+
+ var types = module.Compiler.BuiltinTypes;
+ var bool_type = types.Bool;
+ var nullable_bool = nullable.MakeGenericType (module, new [] { bool_type });
+ var nullable_int = nullable.MakeGenericType (module, new[] { types.Int });
+ var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt });
+ var nullable_long = nullable.MakeGenericType (module, new[] { types.Long });
+ var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong });
+ var nullable_float = nullable.MakeGenericType (module, new[] { types.Float });
+ var nullable_double = nullable.MakeGenericType (module, new[] { types.Double });
+ var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal });
+
+ return new [] {
+ new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.EqualityMask, bool_type),
+ new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.EqualityMask, bool_type),
+ new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.EqualityMask, bool_type),
+ new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.EqualityMask, bool_type),
+ new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.EqualityMask, bool_type),
+ new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.EqualityMask, bool_type),
+ new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.EqualityMask, bool_type),
+ new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.EqualityMask, bool_type)
+ };
}
//
// 7.2.6.2 Binary numeric promotions
//
- public bool DoBinaryOperatorPromotion (ResolveContext ec)
+ bool DoBinaryOperatorPromotion (ResolveContext rc)
{
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);
+ if (ltype.IsNullableType) {
+ ltype = Nullable.NullableInfo.GetUnderlyingType (ltype);
}
- 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);
+ //
+ // This is numeric promotion code only
+ //
+ if (ltype.BuiltinType == BuiltinTypeSpec.Type.Bool)
+ return true;
- if (temp == null)
- return false;
- left = temp;
+ TypeSpec rtype = right.Type;
+ if (rtype.IsNullableType) {
+ rtype = Nullable.NullableInfo.GetUnderlyingType (rtype);
}
- if (rtype != int32) {
- Constant c = right as Constant;
- if (c != null)
- temp = c.ConvertImplicitly (int32);
- else
- temp = Convert.ImplicitNumericConversion (right, int32);
+ var lb = ltype.BuiltinType;
+ var rb = rtype.BuiltinType;
+ TypeSpec type;
+ Expression expr;
+
+ if (lb == BuiltinTypeSpec.Type.Decimal || rb == BuiltinTypeSpec.Type.Decimal) {
+ type = rc.BuiltinTypes.Decimal;
+ } else if (lb == BuiltinTypeSpec.Type.Double || rb == BuiltinTypeSpec.Type.Double) {
+ type = rc.BuiltinTypes.Double;
+ } else if (lb == BuiltinTypeSpec.Type.Float || rb == BuiltinTypeSpec.Type.Float) {
+ type = rc.BuiltinTypes.Float;
+ } else if (lb == BuiltinTypeSpec.Type.ULong || rb == BuiltinTypeSpec.Type.ULong) {
+ type = rc.BuiltinTypes.ULong;
+
+ if (IsSignedType (lb)) {
+ expr = ConvertSignedConstant (left, type);
+ if (expr == null)
+ return false;
+ left = expr;
+ } else if (IsSignedType (rb)) {
+ expr = ConvertSignedConstant (right, type);
+ if (expr == null)
+ return false;
+ right = expr;
+ }
+
+ } else if (lb == BuiltinTypeSpec.Type.Long || rb == BuiltinTypeSpec.Type.Long) {
+ type = rc.BuiltinTypes.Long;
+ } else if (lb == BuiltinTypeSpec.Type.UInt || rb == BuiltinTypeSpec.Type.UInt) {
+ type = rc.BuiltinTypes.UInt;
+
+ if (IsSignedType (lb)) {
+ expr = ConvertSignedConstant (left, type);
+ if (expr == null)
+ type = rc.BuiltinTypes.Long;
+ } else if (IsSignedType (rb)) {
+ expr = ConvertSignedConstant (right, type);
+ if (expr == null)
+ type = rc.BuiltinTypes.Long;
+ }
+ } else {
+ type = rc.BuiltinTypes.Int;
+ }
- if (temp == null)
+ if (ltype != type) {
+ expr = PromoteExpression (rc, left, type);
+ if (expr == null)
return false;
- right = temp;
+
+ left = expr;
+ }
+
+ if (rtype != type) {
+ expr = PromoteExpression (rc, right, type);
+ if (expr == null)
+ return false;
+
+ right = expr;
}
return true;
}
+ static bool IsSignedType (BuiltinTypeSpec.Type type)
+ {
+ switch (type) {
+ case BuiltinTypeSpec.Type.Int:
+ case BuiltinTypeSpec.Type.Short:
+ case BuiltinTypeSpec.Type.SByte:
+ case BuiltinTypeSpec.Type.Long:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static Expression ConvertSignedConstant (Expression expr, TypeSpec type)
+ {
+ var c = expr as Constant;
+ if (c == null)
+ return null;
+
+ return c.ConvertImplicitly (type);
+ }
+
+ static Expression PromoteExpression (ResolveContext rc, Expression expr, TypeSpec type)
+ {
+ if (expr.Type.IsNullableType) {
+ return Convert.ImplicitConversionStandard (rc, expr,
+ rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc, new[] { type }), expr.Location);
+ }
+
+ var c = expr as Constant;
+ if (c != null)
+ return c.ConvertImplicitly (type);
+
+ return Convert.ImplicitNumericConversion (expr, type);
+ }
+
protected override Expression DoResolve (ResolveContext ec)
{
if (left == null)
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 lc = left as Constant;
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);
+ lc = EnumLiftUp (ec, lc, rc);
if (lc != null)
- rc = EnumLiftUp (ec, rc, lc, loc);
+ rc = EnumLiftUp (ec, rc, lc);
}
if (rc != null && lc != null) {
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;
- }
+ if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic || right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
+ return DoResolveDynamic (ec);
+
+ return DoResolveCore (ec, left, right);
+ }
- Arguments args;
+ Expression DoResolveDynamic (ResolveContext rc)
+ {
+ 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 (rc, left, right);
+ return null;
+ }
- //
- // 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;
+ Arguments args;
- args = new Arguments (2);
+ //
+ // 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;
- if (lt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
- LocalVariable temp = LocalVariable.CreateCompilerGenerated (lt, ec.CurrentBlock, loc);
+ args = new Arguments (2);
- var cond_args = new Arguments (1);
- cond_args.Add (new Argument (new SimpleAssign (temp.CreateReferenceExpression (ec, loc), left).Resolve (ec)));
+ if (lt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
+ LocalVariable temp = LocalVariable.CreateCompilerGenerated (lt, rc.CurrentBlock, loc);
- //
- // 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;
- }
+ var cond_args = new Arguments (1);
+ cond_args.Add (new Argument (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left).Resolve (rc)));
- args.Add (new Argument (left));
- args.Add (new Argument (right));
- cond_right = new DynamicExpressionStatement (this, args, loc);
+ //
+ // dynamic && bool => IsFalse (temp = left) ? temp : temp && right;
+ // dynamic || bool => IsTrue (temp = left) ? temp : temp || right;
+ //
+ left = temp.CreateReferenceExpression (rc, loc);
+ if (oper == Operator.LogicalAnd) {
+ expr = DynamicUnaryConversion.CreateIsFalse (rc, cond_args, loc);
+ cond_left = left;
} else {
- LocalVariable temp = LocalVariable.CreateCompilerGenerated (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
+ expr = DynamicUnaryConversion.CreateIsTrue (rc, cond_args, loc);
+ cond_left = left;
+ }
- args.Add (new Argument (temp.CreateReferenceExpression (ec, loc).Resolve (ec)));
- args.Add (new Argument (right));
- right = new DynamicExpressionStatement (this, args, loc);
+ args.Add (new Argument (left));
+ args.Add (new Argument (right));
+ cond_right = new DynamicExpressionStatement (this, args, loc);
+ } else {
+ LocalVariable temp = LocalVariable.CreateCompilerGenerated (rc.BuiltinTypes.Bool, rc.CurrentBlock, 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;
- }
+ args.Add (new Argument (temp.CreateReferenceExpression (rc, loc).Resolve (rc)));
+ args.Add (new Argument (right));
+ right = new DynamicExpressionStatement (this, args, loc);
- expr = new BooleanExpression (new SimpleAssign (temp.CreateReferenceExpression (ec, loc), left));
+ //
+ // 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 (rc, loc);
+ } else {
+ cond_left = temp.CreateReferenceExpression (rc, loc);
+ cond_right = right;
}
- return new Conditional (expr, cond_left, cond_right, loc).Resolve (ec);
+ expr = new BooleanExpression (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left));
}
- 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);
- lifted.state = state;
- return lifted.Resolve (ec);
+ return new Conditional (expr, cond_left, cond_right, loc).Resolve (rc);
}
- return DoResolveCore (ec, left, right);
+ args = new Arguments (2);
+ args.Add (new Argument (left));
+ args.Add (new Argument (right));
+ return new DynamicExpressionStatement (this, args, loc).Resolve (rc);
}
- protected Expression DoResolveCore (ResolveContext ec, Expression left_orig, Expression right_orig)
+ Expression DoResolveCore (ResolveContext ec, Expression left_orig, Expression right_orig)
{
Expression expr = ResolveOperator (ec);
if (expr == null)
}
public override SLE.Expression MakeExpression (BuilderContext ctx)
+ {
+ return MakeExpression (ctx, left, right);
+ }
+
+ public SLE.Expression MakeExpression (BuilderContext ctx, Expression left, Expression right)
{
var le = left.MakeExpression (ctx);
var re = right.MakeExpression (ctx);
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);
+ Expression expr = new UserOperatorCall (method, args, CreateExpressionTree, loc);
return new ClassCast (expr, l);
}
//
- // Enumeration operators
+ // Resolves enumeration operators where only single predefined overload exists, handles lifted versions too
//
- Expression ResolveOperatorEnum (ResolveContext ec, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype)
+ Expression ResolveSingleEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype)
{
//
// bool 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;
+ if ((oper & Operator.ComparisonMask) != 0) {
+ type = rc.BuiltinTypes.Bool;
+ } else {
+ if (lenum)
+ type = ltype;
+ else if (renum)
+ type = rtype;
+ else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum)
+ type = ltype;
+ else
+ type = rtype;
+ }
- 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;
+ if (ltype == rtype) {
+ if (lenum || renum)
+ return this;
+ var lifted = new Nullable.LiftedBinaryOperator (this);
+ lifted.Left = left;
+ lifted.Right = right;
+ return lifted.Resolve (rc);
+ }
+
+ if (renum && !ltype.IsNullableType) {
+ expr = Convert.ImplicitConversion (rc, left, rtype, loc);
+ if (expr != null) {
+ left = expr;
+ return this;
+ }
+ } else if (lenum && !rtype.IsNullableType) {
+ expr = Convert.ImplicitConversion (rc, right, ltype, loc);
+ if (expr != null) {
right = expr;
- rtype = expr.Type;
- } else {
- return null;
+ return this;
}
+ }
- 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;
- }
+ //
+ // Now try lifted version of predefined operator
+ //
+ var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec;
+ if (nullable_type != null) {
+ if (renum && !ltype.IsNullableType) {
+ var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { rtype });
+ expr = Convert.ImplicitConversion (rc, left, lifted_type, loc);
+ if (expr != null) {
left = expr;
- } else {
- res_type = underlying_type;
+ right = Convert.ImplicitConversion (rc, right, lifted_type, loc);
}
- 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;
+ if ((oper & Operator.BitwiseMask) != 0)
+ type = lifted_type;
- res_type = ltype;
- } else {
- res_type = underlying_type;
+ if (left.IsNull) {
+ if ((oper & Operator.BitwiseMask) != 0)
+ return Nullable.LiftedNull.CreateFromExpression (rc, this);
+
+ return CreateLiftedValueTypeResult (rc, rtype);
}
- 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 (expr != null) {
+ var lifted = new Nullable.LiftedBinaryOperator (this);
+ lifted.Left = expr;
+ lifted.Right = right;
+ return lifted.Resolve (rc);
+ }
+ } else if (lenum && !rtype.IsNullableType) {
+ var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { ltype });
+
+ expr = Convert.ImplicitConversion (rc, right, lifted_type, loc);
+ if (expr != null) {
+ right = expr;
+ left = Convert.ImplicitConversion (rc, left, lifted_type, loc);
+ }
- if (rtype != underlying_type && (state & (State.RightNullLifted | State.LeftNullLifted)) == 0) {
- expr = Convert.ImplicitConversion (ec, right, underlying_type, right.Location);
+ if ((oper & Operator.BitwiseMask) != 0)
+ type = lifted_type;
+
+ if (right.IsNull) {
+ if ((oper & Operator.BitwiseMask) != 0)
+ return Nullable.LiftedNull.CreateFromExpression (rc, this);
+
+ return CreateLiftedValueTypeResult (rc, ltype);
+ }
+
+ if (expr != null) {
+ var lifted = new Nullable.LiftedBinaryOperator (this);
+ lifted.Left = left;
+ lifted.Right = expr;
+ return lifted.Resolve (rc);
+ }
+ } else if (rtype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (rtype).IsEnum) {
+ if (left.IsNull) {
+ if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
+ left = Convert.ImplicitConversion (rc, left, rtype, left.Location);
+
+ if ((oper & Operator.RelationalMask) != 0)
+ return CreateLiftedValueTypeResult (rc, rtype);
+
+ if ((oper & Operator.BitwiseMask) != 0)
+ return Nullable.LiftedNull.CreateFromExpression (rc, this);
+
+ // Equality operators are valid between E? and null
+ expr = left;
+ } else {
+ expr = Convert.ImplicitConversion (rc, left, Nullable.NullableInfo.GetUnderlyingType (rtype), loc);
if (expr == null)
return null;
+ }
- right = expr;
+ if (expr != null) {
+ var lifted = new Nullable.LiftedBinaryOperator (this);
+ lifted.Left = expr;
+ lifted.Right = right;
+ return lifted.Resolve (rc);
}
- } else {
- underlying_type = EnumSpec.GetUnderlyingType (rtype);
- res_type = rtype;
- if (ltype != underlying_type) {
- expr = Convert.ImplicitConversion (ec, left, underlying_type, left.Location);
+ } else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum) {
+ if (right.IsNull) {
+ if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
+ right = Convert.ImplicitConversion (rc, right, ltype, right.Location);
+
+ if ((oper & Operator.RelationalMask) != 0)
+ return CreateLiftedValueTypeResult (rc, ltype);
+
+ if ((oper & Operator.BitwiseMask) != 0)
+ return Nullable.LiftedNull.CreateFromExpression (rc, this);
+
+ // Equality operators are valid between E? and null
+ expr = right;
+ } else {
+ expr = Convert.ImplicitConversion (rc, right, Nullable.NullableInfo.GetUnderlyingType (ltype), loc);
if (expr == null)
return null;
+ }
- left = expr;
+ if (expr != null) {
+ var lifted = new Nullable.LiftedBinaryOperator (this);
+ lifted.Left = left;
+ lifted.Right = expr;
+ return lifted.Resolve (rc);
}
}
-
- 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);
+ return null;
+ }
+
+ static Expression ConvertEnumOperandToUnderlyingType (ResolveContext rc, Expression expr)
+ {
+ TypeSpec underlying_type;
+ if (expr.Type.IsNullableType) {
+ var nt = Nullable.NullableInfo.GetUnderlyingType (expr.Type);
+ if (nt.IsEnum)
+ underlying_type = EnumSpec.GetUnderlyingType (nt);
else
- left = EmptyCast.Create (left, underlying_type);
+ underlying_type = nt;
+ } else if (expr.Type.IsEnum) {
+ underlying_type = EnumSpec.GetUnderlyingType (expr.Type);
+ } else {
+ underlying_type = expr.Type;
}
- if (right.Type != underlying_type) {
- if (right is Constant)
- right = ((Constant) right).ConvertExplicitly (false, underlying_type);
- else
- right = EmptyCast.Create (right, underlying_type);
+ switch (underlying_type.BuiltinType) {
+ case BuiltinTypeSpec.Type.SByte:
+ case BuiltinTypeSpec.Type.Byte:
+ case BuiltinTypeSpec.Type.Short:
+ case BuiltinTypeSpec.Type.UShort:
+ underlying_type = rc.BuiltinTypes.Int;
+ break;
}
+ if (expr.Type.IsNullableType)
+ underlying_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { underlying_type });
+
+ if (expr.Type == underlying_type)
+ return expr;
+
+ return EmptyCast.Create (expr, underlying_type);
+ }
+
+ Expression ResolveEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype)
+ {
+ //
+ // U operator - (E e, E f)
+ // E operator - (E e, U x) // Internal decomposition operator
+ // E operator - (U x, E e) // Internal decomposition operator
//
- // C# specification uses explicit cast syntax which means binary promotion
- // should happen, however it seems that csc does not do that
+ // E operator + (E e, U x)
+ // E operator + (U x, E e)
//
- if (!DoBinaryOperatorPromotion (ec)) {
- left = ltemp;
- right = rtemp;
- return null;
+
+ TypeSpec enum_type;
+
+ if (lenum)
+ enum_type = ltype;
+ else if (renum)
+ enum_type = rtype;
+ else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum)
+ enum_type = ltype;
+ else
+ enum_type = rtype;
+
+ Expression expr;
+ if (!enum_type.IsNullableType) {
+ expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, false), false);
+ if (expr != null) {
+ if (oper == Operator.Subtraction)
+ expr = ConvertEnumSubtractionResult (rc, expr);
+ else
+ expr = ConvertEnumAdditionalResult (expr, enum_type);
+
+ AddEnumResultCast (expr.Type);
+
+ return expr;
+ }
+
+ enum_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { enum_type });
}
- if (underlying_type_result != null && left.Type != underlying_type_result) {
- enum_conversion = Convert.ExplicitNumericConversion (ec, new EmptyExpression (left.Type), underlying_type_result);
+ expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, true), false);
+ if (expr != null) {
+ if (oper == Operator.Subtraction)
+ expr = ConvertEnumSubtractionResult (rc, expr);
+ else
+ expr = ConvertEnumAdditionalResult (expr, enum_type);
+
+ AddEnumResultCast (expr.Type);
}
- expr = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryStandard, true, res_type);
- if (expr == null)
- return null;
+ return expr;
+ }
- if (!IsCompound)
- return expr;
+ static Expression ConvertEnumAdditionalResult (Expression expr, TypeSpec enumType)
+ {
+ return EmptyCast.Create (expr, enumType);
+ }
+ Expression ConvertEnumSubtractionResult (ResolveContext rc, Expression expr)
+ {
//
- // Section: 7.16.2
+ // Enumeration subtraction has different result type based on
+ // best overload
//
+ TypeSpec result_type;
+ if (left.Type == right.Type) {
+ var c = right as EnumConstant;
+ if (c != null && c.IsZeroInteger && !right.Type.IsEnum) {
+ //
+ // LAMESPEC: This is quite unexpected for expression E - 0 the return type is
+ // E which is not what expressions E - 1 or 0 - E return
+ //
+ result_type = left.Type;
+ } else {
+ result_type = left.Type.IsNullableType ?
+ Nullable.NullableInfo.GetEnumUnderlyingType (rc.Module, left.Type) :
+ EnumSpec.GetUnderlyingType (left.Type);
+ }
+ } else {
+ if (IsEnumOrNullableEnum (left.Type)) {
+ result_type = left.Type;
+ } else {
+ result_type = right.Type;
+ }
- //
- // If the return type of the selected operator is implicitly convertible to the type of x
- //
- if (Convert.ImplicitConversionExists (ec, expr, ltype))
- return expr;
+ if (expr is Nullable.LiftedBinaryOperator && !result_type.IsNullableType)
+ result_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { result_type });
+ }
- //
- // 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;
+ return EmptyCast.Create (expr, result_type);
+ }
- if (Convert.ImplicitConversionExists (ec, ltemp, ltype))
- return expr;
+ void AddEnumResultCast (TypeSpec type)
+ {
+ if (type.IsNullableType)
+ type = Nullable.NullableInfo.GetUnderlyingType (type);
- return null;
+ if (type.IsEnum)
+ type = EnumSpec.GetUnderlyingType (type);
+
+ switch (type.BuiltinType) {
+ case BuiltinTypeSpec.Type.SByte:
+ enum_conversion = ConvCast.Mode.I4_I1;
+ break;
+ case BuiltinTypeSpec.Type.Byte:
+ enum_conversion = ConvCast.Mode.I4_U1;
+ break;
+ case BuiltinTypeSpec.Type.Short:
+ enum_conversion = ConvCast.Mode.I4_I2;
+ break;
+ case BuiltinTypeSpec.Type.UShort:
+ enum_conversion = ConvCast.Mode.I4_U2;
+ break;
+ }
}
//
- // 7.9.6 Reference type equality operators
+ // Equality operators rules
//
- Expression ResolveOperatorEquality (ResolveContext ec, TypeSpec l, TypeSpec r)
+ Expression ResolveEquality (ResolveContext ec, TypeSpec l, TypeSpec r, bool primitives_only)
{
Expression result;
type = ec.BuiltinTypes.Bool;
+ bool no_arg_conv = false;
- //
- // 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 (!primitives_only) {
- if (!tparam_l.IsReferenceType)
- return null;
+ //
+ // 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) {
+ if (tparam_l.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType)
+ 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;
+ left = new BoxedCast (left, ec.BuiltinTypes.Object);
+ return this;
+ }
- return this;
- }
+ if (!tparam_l.IsReferenceType)
+ return null;
- if (tparam_r != null) {
- if (left is NullLiteral && !tparam_r.HasSpecialStruct) {
- right = new BoxedCast (right, ec.BuiltinTypes.Object);
- return this;
+ l = tparam_l.GetEffectiveBase ();
+ left = new BoxedCast (left, l);
+ } else if (left is NullLiteral && tparam_r == null) {
+ if (TypeSpec.IsReferenceType (r))
+ return this;
+
+ if (r.Kind == MemberKind.InternalCompilerType)
+ return null;
}
- if (!tparam_r.IsReferenceType)
- return null;
+ if (tparam_r != null) {
+ if (left is NullLiteral) {
+ if (tparam_r.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType)
+ 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;
+ right = new BoxedCast (right, ec.BuiltinTypes.Object);
+ return this;
+ }
- return this;
- }
+ if (!tparam_r.IsReferenceType)
+ return null;
- bool no_arg_conv = false;
+ r = tparam_r.GetEffectiveBase ();
+ right = new BoxedCast (right, r);
+ } else if (right is NullLiteral) {
+ if (TypeSpec.IsReferenceType (l))
+ return this;
- //
- // 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 (l.Kind == MemberKind.InternalCompilerType)
+ return null;
+ }
+
+ //
+ // 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;
- right = result;
- r = l;
- } else if (r.IsDelegate && l != r) {
- return null;
+ left = result;
+ l = r;
+ } else {
+ no_arg_conv = l == r && !l.IsStruct;
}
- } 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;
}
//
// 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);
+ result = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryEquality, no_arg_conv);
if (result != null)
return result;
+
+ //
+ // Now try lifted version of predefined operators
+ //
+ if (no_arg_conv && !l.IsNullableType) {
+ //
+ // Optimizes cases which won't match
+ //
+ } else {
+ result = ResolveOperatorPredefined (ec, ec.Module.OperatorsBinaryEqualityLifted, no_arg_conv);
+ if (result != null)
+ return result;
+ }
+
+ //
+ // The == and != operators permit one operand to be a value of a nullable
+ // type and the other to be the null literal, even if no predefined or user-defined
+ // operator (in unlifted or lifted form) exists for the operation.
+ //
+ if ((l.IsNullableType && right.IsNull) || (r.IsNullableType && left.IsNull)) {
+ var lifted = new Nullable.LiftedBinaryOperator (this);
+ lifted.Left = left;
+ lifted.Right = right;
+ return lifted.Resolve (ec);
+ }
}
//
return this;
}
- return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryUnsafe, false, null);
+ return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryUnsafe, false);
}
//
// Build-in operators method overloading
//
- protected virtual Expression ResolveOperatorPredefined (ResolveContext ec, PredefinedOperator [] operators, bool primitives_only, TypeSpec enum_type)
+ Expression ResolveOperatorPredefined (ResolveContext ec, PredefinedOperator [] operators, bool primitives_only)
{
PredefinedOperator best_operator = null;
TypeSpec l = left.Type;
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));
+ OperName (oper), l.GetSignatureForError (), r.GetSignatureForError ());
best_operator = po;
break;
if (best_operator == null)
return null;
- Expression expr = best_operator.ConvertResult (ec, this);
+ return 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);
+ //
+ // Optimize & constant expressions with 0 value
+ //
+ Expression OptimizeAndOperation (Expression expr)
+ {
+ Constant rc = right as Constant;
+ Constant lc = left as Constant;
+ if ((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue)) {
+ //
+ // 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);
- }
+ return ReducedExpression.Create (side_effect, expr);
}
- if (enum_type == null)
- return expr;
+ return expr;
+ }
- //
- // HACK: required by enum_conversion
- //
- expr.Type = enum_type;
- return EmptyCast.Create (expr, enum_type);
+ //
+ // Value types can be compared with the null literal because of the lifting
+ // language rules. However the result is always true or false.
+ //
+ public Expression CreateLiftedValueTypeResult (ResolveContext rc, TypeSpec valueType)
+ {
+ if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
+ type = rc.BuiltinTypes.Bool;
+ return this;
+ }
+
+ // FIXME: Handle side effect constants
+ Constant c = new BoolConstant (rc.BuiltinTypes, Oper == Operator.Inequality, loc);
+
+ if ((Oper & Operator.EqualityMask) != 0) {
+ rc.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is always `{1}'",
+ valueType.GetSignatureForError (), c.GetValueAsLiteral ());
+ } else {
+ rc.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'",
+ valueType.GetSignatureForError (), c.GetValueAsLiteral ());
+ }
+
+ return c;
}
//
// Performs user-operator overloading
//
- protected virtual Expression ResolveUserOperator (ResolveContext ec, Expression left, Expression right)
+ Expression ResolveUserOperator (ResolveContext rc, Expression left, Expression right)
{
+ Expression oper_expr;
+
var op = ConvertBinaryToUserOperator (oper);
var l = left.Type;
if (l.IsNullableType)
Arguments args = new Arguments (2);
Argument larg = new Argument (left);
- args.Add (larg);
+ args.Add (larg);
Argument rarg = new Argument (right);
args.Add (rarg);
left_operators = right_operators;
}
- var res = new OverloadResolver (left_operators, OverloadResolver.Restrictions.ProbingOnly |
- OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded, loc);
+ const OverloadResolver.Restrictions restr = OverloadResolver.Restrictions.ProbingOnly |
+ OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded;
- var oper_method = res.ResolveOperator (ec, ref args);
- if (oper_method == null)
- return null;
+ var res = new OverloadResolver (left_operators, restr, loc);
- 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])
+ var oper_method = res.ResolveOperator (rc, ref args);
+ if (oper_method == null) {
+ //
+ // Logical && and || cannot be lifted
+ //
+ if ((oper & Operator.LogicalMask) != 0)
+ return null;
+
+ //
+ // Apply lifted user operators only for liftable types. Implicit conversion
+ // to nullable types is not allowed
+ //
+ if (!IsLiftedOperatorApplicable ())
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);
+ // TODO: Cache the result in module container
+ var lifted_methods = CreateLiftedOperators (rc, left_operators);
+ if (lifted_methods == null)
+ return null;
+
+ res = new OverloadResolver (lifted_methods, restr | OverloadResolver.Restrictions.ProbingOnly, loc);
+
+ oper_method = res.ResolveOperator (rc, ref args);
+ if (oper_method == null)
+ return null;
+
+ MethodSpec best_original = null;
+ foreach (MethodSpec ms in left_operators) {
+ if (ms.MemberDefinition == oper_method.MemberDefinition) {
+ best_original = ms;
+ break;
+ }
}
- }
- Expression oper_expr;
+ if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
+ //
+ // Expression trees use lifted notation in this case
+ //
+ this.left = Convert.ImplicitConversion (rc, left, oper_method.Parameters.Types[0], left.Location);
+ this.right = Convert.ImplicitConversion (rc, right, oper_method.Parameters.Types[1], left.Location);
+ }
+
+ var ptypes = best_original.Parameters.Types;
+
+ if (left.IsNull || right.IsNull) {
+ //
+ // The lifted operator produces the value false if one or both operands are null for
+ // relational operators.
+ //
+ if ((oper & Operator.ComparisonMask) != 0) {
+ //
+ // CSC BUG: This should be different warning, csc reports CS0458 with bool? which is wrong
+ // because return type is actually bool
+ //
+ // For some reason CSC does not report this warning for equality operators
+ //
+ return CreateLiftedValueTypeResult (rc, left.IsNull ? ptypes [1] : ptypes [0]);
+ }
+
+ // The lifted operator produces a null value if one or both operands are null
+ //
+ if ((oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0) {
+ type = oper_method.ReturnType;
+ return Nullable.LiftedNull.CreateFromExpression (rc, this);
+ }
+ }
- // TODO: CreateExpressionTree is allocated every time
+ type = oper_method.ReturnType;
+ var lifted = new Nullable.LiftedBinaryOperator (this);
+ lifted.UserOperator = best_original;
+
+ if (left.Type.IsNullableType && !ptypes[0].IsNullableType) {
+ lifted.UnwrapLeft = new Nullable.Unwrap (left);
+ }
+
+ if (right.Type.IsNullableType && !ptypes[1].IsNullableType) {
+ lifted.UnwrapRight = new Nullable.Unwrap (right);
+ }
+
+ lifted.Left = Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? left, ptypes[0], left.Location);
+ lifted.Right = Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? right, ptypes[1], right.Location);
+
+ return lifted.Resolve (rc);
+ }
+
if ((oper & Operator.LogicalMask) != 0) {
+ // TODO: CreateExpressionTree is allocated every time
oper_expr = new ConditionalLogicalOperator (oper_method, args, CreateExpressionTree,
- oper == Operator.LogicalAnd, loc).Resolve (ec);
+ oper == Operator.LogicalAnd, loc).Resolve (rc);
} else {
oper_expr = new UserOperatorCall (oper_method, args, CreateExpressionTree, loc);
}
- if (!llifted)
- this.left = larg.Expr;
-
- if (!rlifted)
- this.right = rarg.Expr;
+ this.left = larg.Expr;
+ this.right = rarg.Expr;
return oper_expr;
}
+ bool IsLiftedOperatorApplicable ()
+ {
+ if (left.Type.IsNullableType) {
+ if ((oper & Operator.EqualityMask) != 0)
+ return !right.IsNull;
+
+ return true;
+ }
+
+ if (right.Type.IsNullableType) {
+ if ((oper & Operator.EqualityMask) != 0)
+ return !left.IsNull;
+
+ return true;
+ }
+
+ if (TypeSpec.IsValueType (left.Type))
+ return right.IsNull;
+
+ if (TypeSpec.IsValueType (right.Type))
+ return left.IsNull;
+
+ return false;
+ }
+
+ List<MemberSpec> CreateLiftedOperators (ResolveContext rc, IList<MemberSpec> operators)
+ {
+ var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec;
+ if (nullable_type == null)
+ return null;
+
+ //
+ // Lifted operators permit predefined and user-defined operators that operate
+ // on non-nullable value types to also be used with nullable forms of those types.
+ // Lifted operators are constructed from predefined and user-defined operators
+ // that meet certain requirements
+ //
+ List<MemberSpec> lifted = null;
+ foreach (MethodSpec oper in operators) {
+ TypeSpec rt;
+ if ((Oper & Operator.ComparisonMask) != 0) {
+ //
+ // Result type must be of type bool for lifted comparison operators
+ //
+ rt = oper.ReturnType;
+ if (rt.BuiltinType != BuiltinTypeSpec.Type.Bool)
+ continue;
+ } else {
+ if (!TypeSpec.IsNonNullableValueType (oper.ReturnType))
+ continue;
+
+ rt = null;
+ }
+
+ var ptypes = oper.Parameters.Types;
+ if (!TypeSpec.IsNonNullableValueType (ptypes [0]) || !TypeSpec.IsNonNullableValueType (ptypes [1]))
+ continue;
+
+ //
+ // LAMESPEC: I am not sure why but for equality operators to be lifted
+ // both types have to match
+ //
+ if ((Oper & Operator.EqualityMask) != 0 && ptypes [0] != ptypes [1])
+ continue;
+
+ if (lifted == null)
+ lifted = new List<MemberSpec> ();
+
+ //
+ // The lifted form is constructed by adding a single ? modifier to each operand and
+ // result type except for comparison operators where return type is bool
+ //
+ if (rt == null)
+ rt = nullable_type.MakeGenericType (rc.Module, new[] { oper.ReturnType });
+
+ var parameters = ParametersCompiled.CreateFullyResolved (
+ nullable_type.MakeGenericType (rc.Module, new [] { ptypes[0] }),
+ nullable_type.MakeGenericType (rc.Module, new [] { ptypes[1] }));
+
+ var lifted_op = new MethodSpec (oper.Kind, oper.DeclaringType, oper.MemberDefinition,
+ rt, parameters, oper.Modifiers);
+
+ lifted.Add (lifted_op);
+ }
+
+ return lifted;
+ }
+
//
// Merge two sets of user operators into one, they are mostly distinguish
// except when they share base type and it contains an operator
} 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));
+ type.GetSignatureForError ());
}
}
}
/// </remarks>
public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
{
+ if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) {
+ left = left.EmitToField (ec);
+
+ if ((oper & Operator.LogicalMask) == 0) {
+ right = right.EmitToField (ec);
+ }
+ }
+
//
// This is more complicated than it looks, but its just to avoid
// duplicated tests: basically, we allow ==, !=, >, <, >= and <=
}
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);
}
}
+ EmitOperator (ec, left, right);
+ }
+
+ public void EmitOperator (EmitContext ec, Expression left, Expression right)
+ {
left.Emit (ec);
right.Emit (ec);
- EmitOperatorOpcode (ec, oper, l);
+
+ EmitOperatorOpcode (ec, oper, left.Type, right);
//
- // Nullable enum could require underlying type cast and we cannot simply wrap binary
- // expression because that would wrap lifted binary operation
+ // Emit result enumerable conversion this way because it's quite complicated get it
+ // to resolved tree because expression tree cannot see it.
//
- if (enum_conversion != null)
- enum_conversion.Emit (ec);
+ if (enum_conversion != 0)
+ ConvCast.Emit (ec, enum_conversion);
}
public override void EmitSideEffect (EmitContext ec)
return CreateExpressionTree (ec, null);
}
- Expression CreateExpressionTree (ResolveContext ec, Expression method)
+ public Expression CreateExpressionTree (ResolveContext ec, Expression method)
{
string method_name;
bool lift_arg = false;
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ arguments.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
if (arguments.Count != 2)
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 ());
+ type.GetSignatureForError (), oper.GetSignatureForError ());
return null;
}
public class PointerArithmetic : Expression {
Expression left, right;
- Binary.Operator op;
+ readonly Binary.Operator op;
//
// We assume that `l' is always a pointer
if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long || rtype.BuiltinType == BuiltinTypeSpec.Type.ULong)
ec.Emit (OpCodes.Conv_I8);
- Binary.EmitOperatorOpcode (ec, Binary.Operator.Multiply, rtype);
+ Binary.EmitOperatorOpcode (ec, Binary.Operator.Multiply, rtype, right);
}
if (left_const == null) {
else if (rtype.BuiltinType == BuiltinTypeSpec.Type.ULong)
ec.Emit (OpCodes.Conv_U);
- Binary.EmitOperatorOpcode (ec, op, op_type);
+ Binary.EmitOperatorOpcode (ec, op, op_type, right);
}
}
}
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);
- }
+ true_expr = true_expr.Resolve (ec);
+ false_expr = false_expr.Resolve (ec);
if (true_expr == null || false_expr == null || expr == null)
return null;
} 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));
+ true_type.GetSignatureForError (), false_type.GetSignatureForError ());
return null;
}
- }
+ }
+ Constant c = expr as Constant;
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");
+ // CSC: Missing warning
+ Warning_UnreachableExpression (ec, is_false ? true_expr.Location : false_expr.Location);
}
return ReducedExpression.Create (
ec.MarkLabel (end_target);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ var expr_fc = fc.DefiniteAssignment;
+
+ fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_fc);
+ true_expr.FlowAnalysis (fc);
+ var true_fc = fc.DefiniteAssignment;
+
+ fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_fc);
+ false_expr.FlowAnalysis (fc);
+
+ fc.DefiniteAssignment &= true_fc;
+ }
+
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Conditional target = (Conditional) t;
#region Abstract
public abstract HoistedVariable GetHoistedVariable (AnonymousExpression ae);
public abstract void SetHasAddressTaken ();
- public abstract void VerifyAssigned (ResolveContext rc);
public abstract bool IsLockedByStatement { get; set; }
#endregion
- public override void VerifyAssigned (ResolveContext rc)
+ public override void FlowAnalysis (FlowAnalysisContext fc)
{
- VariableInfo variable_info = local_info.VariableInfo;
+ VariableInfo variable_info = VariableInfo;
if (variable_info == null)
return;
- if (variable_info.IsAssigned (rc))
+ if (fc.IsDefinitelyAssigned (variable_info))
return;
- rc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name);
- variable_info.SetAssigned (rc);
+ fc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name);
+ variable_info.SetAssigned (fc.DefiniteAssignment, true);
}
public override void SetHasAddressTaken ()
{
local_info.SetIsUsed ();
- VerifyAssigned (ec);
-
DoResolveBase (ec);
return this;
}
local_info.SetIsUsed ();
if (local_info.IsReadonly && !ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.UsingInitializerScope)) {
- int code;
- string msg;
- if (rhs == EmptyExpression.OutAccess) {
- code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
- } else if (rhs == EmptyExpression.LValueMemberAccess) {
- code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
- } else if (rhs == EmptyExpression.LValueMemberOutAccess) {
- code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
- } else if (rhs == EmptyExpression.UnaryAddress) {
- code = 459; msg = "Cannot take the address of {1} `{0}'";
+ if (rhs == EmptyExpression.LValueMemberAccess) {
+ // CS1654 already reported
} else {
- code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
+ int code;
+ string msg;
+ if (rhs == EmptyExpression.OutAccess) {
+ code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
+ } else if (rhs == EmptyExpression.LValueMemberOutAccess) {
+ code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
+ } else if (rhs == 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 ());
}
- ec.Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
- } else if (VariableInfo != null) {
- VariableInfo.SetAssigned (ec);
}
if (eclass == ExprClass.Unresolved)
Parameter.HasAddressTaken = true;
}
- void SetAssigned (ResolveContext ec)
- {
- if (Parameter.HoistedVariant != null)
- Parameter.HoistedVariant.IsAssigned = true;
-
- if (HasOutModifier && ec.DoFlowAnalysis)
- ec.CurrentBranching.SetAssigned (VariableInfo);
- }
-
bool DoResolveBase (ResolveContext ec)
{
if (eclass != ExprClass.Unresolved)
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;
}
if (!DoResolveBase (ec))
return null;
- SetAssigned (ec);
+ if (Parameter.HoistedVariant != null)
+ Parameter.HoistedVariant.IsAssigned = true;
+
return base.DoResolveLValue (ec, right_side);
}
- public override void VerifyAssigned (ResolveContext rc)
+ public override void FlowAnalysis (FlowAnalysisContext fc)
{
- // HACK: Variables are not captured in probing mode
- if (rc.IsInProbingMode)
+ VariableInfo variable_info = VariableInfo;
+ if (variable_info == null)
return;
- if (HasOutModifier && !VariableInfo.IsAssigned (rc)) {
- rc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
- }
+ if (fc.IsDefinitelyAssigned (variable_info))
+ return;
+
+ fc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
+ fc.SetVariableAssigned (variable_info);
}
}
var emg = MethodGroup as ExtensionMethodGroupExpr;
if (emg != null) {
- return MethodGroupExpr.CreatePredefined (candidate, candidate.DeclaringType, MethodGroup.Location);
+ var mg = MethodGroupExpr.CreatePredefined (candidate, candidate.DeclaringType, MethodGroup.Location);
+ if (candidate.IsGeneric) {
+ var targs = new TypeExpression [candidate.Arity];
+ for (int i = 0; i < targs.Length; ++i) {
+ targs[i] = new TypeExpression (candidate.TypeArguments[i], MethodGroup.Location);
+ }
+
+ mg.SetTypeArguments (null, new TypeArguments (targs));
+ }
+
+ return mg;
}
return MethodGroup;
if (member_expr != null)
member_expr = member_expr.Resolve (ec);
} else {
- member_expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
+ member_expr = expr.Resolve (ec);
}
if (member_expr == null)
} else {
if (member_expr is RuntimeValueExpression) {
ec.Report.Error (Report.RuntimeErrorId, loc, "Cannot invoke a non-delegate type `{0}'",
- member_expr.Type.GetSignatureForError ()); ;
+ member_expr.Type.GetSignatureForError ());
return null;
}
return mg.OverloadResolve (ec, ref arguments, null, OverloadResolver.Restrictions.None);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (mg.IsConditionallyExcluded)
+ return;
+
+ mg.FlowAnalysis (fc);
+
+ if (arguments != null)
+ arguments.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return mg.GetSignatureForError ();
public override void Emit (EmitContext ec)
{
+ if (mg.IsConditionallyExcluded)
+ return;
+
mg.EmitCall (ec, arguments);
}
if (type.IsPointer) {
ec.Report.Error (1919, loc, "Unsafe type `{0}' cannot be used in an object creation expression",
- TypeManager.CSharpName (type));
+ type.GetSignatureForError ());
return null;
}
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));
+ type.GetSignatureForError ());
}
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));
+ type.GetSignatureForError ());
}
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));
+ ec.Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", type.GetSignatureForError ());
return null;
}
}
ec.Report.SymbolRelatedToPreviousError (type);
- ec.Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
+ ec.Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", type.GetSignatureForError ());
return null;
}
}
if (vr != null) {
+ ec.MarkCallEntry (loc);
ec.Emit (OpCodes.Call, method);
return false;
}
}
if (type is TypeParameterSpec)
- return DoEmitTypeParameter (ec);
+ return DoEmitTypeParameter (ec);
+ ec.MarkCallEntry (loc);
ec.Emit (OpCodes.Newobj, method);
return true;
}
ec.Emit (OpCodes.Pop);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (arguments != null)
+ arguments.FlowAnalysis (fc);
+ }
+
public void AddressOf (EmitContext ec, AddressOp mode)
{
EmitAddressOf (ec, mode);
public class ArrayInitializer : Expression
{
List<Expression> elements;
- BlockVariableDeclaration variable;
+ BlockVariable variable;
public ArrayInitializer (List<Expression> init, Location loc)
{
}
}
- public BlockVariableDeclaration VariableDeclaration {
+ public BlockVariable VariableDeclaration {
get {
return variable;
}
{
throw new InternalErrorException ("Missing Resolve call");
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new InternalErrorException ("Missing Resolve call");
+ }
public override object Accept (StructuralVisitor visitor)
{
protected List<Expression> arguments;
protected TypeSpec array_element_type;
- int num_arguments = 0;
+ int num_arguments;
protected int dimensions;
protected readonly ComposedTypeSpecifier rank;
Expression first_emit;
ec.Report.Error (248, loc, "Cannot create an array with a negative size");
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ foreach (var arg in arguments)
+ arg.FlowAnalysis (fc);
+
+ if (array_data != null) {
+ foreach (var ad in array_data)
+ ad.FlowAnalysis (fc);
+ }
+ }
+
bool InitializersContainAwait ()
{
if (array_data == null)
#endregion
- public void CheckStructThisDefiniteAssignment (ResolveContext rc)
+ void CheckStructThisDefiniteAssignment (FlowAnalysisContext fc)
{
//
// It's null for all cases when we don't need to check `this'
if (variable_info == null)
return;
- if (rc.OmitStructFlowAnalysis)
+ if (fc.IsDefinitelyAssigned (variable_info))
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");
- }
+ fc.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)
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ CheckStructThisDefiniteAssignment (fc);
+ }
+
public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
{
if (ae == null)
protected override Expression DoResolve (ResolveContext ec)
{
ResolveBase (ec);
-
- CheckStructThisDefiniteAssignment (ec);
-
return this;
}
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");
{
// Nothing
}
-
- public override void VerifyAssigned (ResolveContext rc)
- {
- }
public override object Accept (StructuralVisitor visitor)
{
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_queried.GetSignatureForError ());
}
type = ec.BuiltinTypes.Int;
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);
- }
- }
+ var e = LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess);
+ if (e != null)
+ e = e.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.Type | ResolveFlags.MethodGroup);
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);
- }
- }
+ var e = LookupNameExpression (rc, MemberLookupRestrictions.None);
- return e;
- }
-
- Expression DoResolveName (ResolveContext rc, Expression right_side)
- {
- Expression e = LookupNameExpression (rc, right_side == null ? MemberLookupRestrictions.ReadAccess : MemberLookupRestrictions.None);
- if (e == null)
+ if (e is TypeExpr) {
+ e.Error_UnexpectedKind (rc, ResolveFlags.VariableOrValue, loc);
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);
}
+ if (e != null)
+ e = e.ResolveLValue (rc, rhs);
+
return e;
}
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);
+ 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);
+ //
+ // 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) {
+ 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)
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);
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);
}
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;
}
var nested = MemberCache.FindNestedType (expr_type, Name, -System.Math.Max (1, Arity));
if (nested != null) {
- Error_TypeArgumentsCannotBeUsed (rc, nested, Arity, expr.Location);
+ Error_TypeArgumentsCannotBeUsed (rc, nested, 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);
+ Error_UnexpectedKind (rc, any_other_member, "type", any_other_member.ExprClassName, loc);
return;
}
if (ec.Module.Compiler.Settings.Version > LanguageVersion.ISO_2 && !ec.IsRuntimeBinder && MethodGroupExpr.IsExtensionMethodArgument (expr)) {
ec.Report.SymbolRelatedToPreviousError (type);
- var cand = ec.Module.GlobalRootNamespace.FindExtensionMethodNamespaces (ec, type, name, Arity);
+ var cand = ec.Module.GlobalRootNamespace.FindExtensionMethodNamespaces (ec, name, Arity);
string missing;
// a using directive or an assembly reference
if (cand != null) {
Expr.EmitBranchable (ec, target, on_true);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ Expr.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
using (ctx.With (BuilderContext.Options.CheckedScope, true)) {
Expr.EmitBranchable (ec, target, on_true);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ Expr.FlowAnalysis (fc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Expression t)
{
UnCheckedExpr target = (UnCheckedExpr) t;
Report.Error (1742, na.Location, "An element access expression cannot use named argument");
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ Expr.FlowAnalysis (fc);
+ Arguments.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return Expr.GetSignatureForError ();
ec.Report.Warning (251, 2, loc, "Indexing an array with a negative index (array indices always start at zero)");
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ ea.FlowAnalysis (fc);
+ }
+
//
// Load the array arguments into the stack.
//
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ // TODO: Check the order
+ base.FlowAnalysis (fc);
+ arguments.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return best_candidate.GetSignatureForError ();
public override void Emit (EmitContext ec)
{
source.Emit (ec);
+ ec.MarkCallEntry (loc);
ec.Emit (OpCodes.Call, method);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ source.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return TypeManager.CSharpSignature (method);
this.loc = loc;
}
+ public CollectionElementInitializer (Location loc)
+ : base (null, null)
+ {
+ this.loc = loc;
+ }
+
public override Expression CreateExpressionTree (ResolveContext ec)
{
Arguments args = new Arguments (2);
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));
+ ec.CurrentInitializerVariable.Type.GetSignatureForError (),
+ ec.BuiltinTypes.IEnumerable.GetSignatureForError ());
return null;
}
is_collection_initialization = true;
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));
+ type.GetSignatureForError ());
}
}
e.EmitStatement (ec);
}
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ foreach (var initializer in initializers)
+ initializer.FlowAnalysis (fc);
+ }
}
//
return instance;
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ base.FlowAnalysis (fc);
+ initializers.FlowAnalysis (fc);
+ }
+
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
eclass = ExprClass.Value;
return this;
}
-
- public override void EmitStatement (EmitContext ec)
- {
- base.EmitStatement (ec);
- }
public override object Accept (StructuralVisitor visitor)
{