From 990aff35fcf14b6cf174e5ff9760e46a9f5f2067 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Wed, 9 Apr 2008 18:47:03 +0000 Subject: [PATCH] 2008-04-09 Marek Safar * convert.cs, ecore.cs, expression.cs, nullable.cs: Initial refactoring of Unary expressions to follow operator overloading rules precisely. Also fixes #321794, #323794 svn path=/trunk/mcs/; revision=100257 --- mcs/mcs/ChangeLog | 8 +- mcs/mcs/convert.cs | 23 +- mcs/mcs/ecore.cs | 2 +- mcs/mcs/expression.cs | 512 ++++++++++++++++++++++-------------------- mcs/mcs/nullable.cs | 5 +- 5 files changed, 294 insertions(+), 256 deletions(-) diff --git a/mcs/mcs/ChangeLog b/mcs/mcs/ChangeLog index 29855087da2..06b2ef99db2 100644 --- a/mcs/mcs/ChangeLog +++ b/mcs/mcs/ChangeLog @@ -1,6 +1,12 @@ +2008-04-09 Marek Safar + + * convert.cs, ecore.cs, expression.cs, nullable.cs: Initial refactoring + of Unary expressions to follow operator overloading rules precisely. + Also fixes #321794, #323794 + 2008-04-08 Marek Safar - * cs-parser.jay, expression.cs: Don't wrap Indirection expression in Unary + * cs-parser.jay, expression.cs: Don't wrap Indirection expression in Unary expression. 2008-04-08 Marek Safar diff --git a/mcs/mcs/convert.cs b/mcs/mcs/convert.cs index e5e4df0cd37..2dedade602d 100644 --- a/mcs/mcs/convert.cs +++ b/mcs/mcs/convert.cs @@ -1073,7 +1073,11 @@ namespace Mono.CSharp { static public Expression ImplicitUserConversion (EmitContext ec, Expression source, Type target, Location loc) { - return UserDefinedConversion (ec, source, target, loc, false); + Expression expr = UserDefinedConversion (ec, source, target, loc, false); + if (expr != null && !TypeManager.IsEqual (expr.Type, target)) + expr = ImplicitConversionStandard (ec, expr, target, loc); + + return expr; } /// @@ -1082,7 +1086,11 @@ namespace Mono.CSharp { static public Expression ExplicitUserConversion (EmitContext ec, Expression source, Type target, Location loc) { - return UserDefinedConversion (ec, source, target, loc, true); + Expression expr = UserDefinedConversion (ec, source, target, loc, true); + if (expr != null && !TypeManager.IsEqual (expr.Type, target)) + expr = ExplicitConversionStandard (ec, expr, target, loc); + + return expr; } static void AddConversionOperators (ArrayList list, @@ -1227,16 +1235,7 @@ namespace Mono.CSharp { if (source == null) return null; - Expression e; - e = new UserCast (method, source, loc); - if (e.Type != target){ - if (!look_for_explicit) - e = ImplicitConversionStandard (ec, e, target, loc); - else - e = ExplicitConversionStandard (ec, e, target, loc); - } - - return e; + return new UserCast (method, source, loc); } /// diff --git a/mcs/mcs/ecore.cs b/mcs/mcs/ecore.cs index 651fca5c182..b3979cc4afc 100644 --- a/mcs/mcs/ecore.cs +++ b/mcs/mcs/ecore.cs @@ -384,7 +384,7 @@ namespace Mono.CSharp { if (b || Convert.ExplicitReferenceConversionExists (Type, target) || Convert.ExplicitUnsafe (e, target) != null || - (ec != null && Convert.UserDefinedConversion (ec, this, target, Location.Null, true) != null)) + (ec != null && Convert.ExplicitUserConversion (ec, this, target, Location.Null) != null)) { Report.Error (266, loc, "Cannot implicitly convert type `{0}' to `{1}'. " + "An explicit conversion exists (are you missing a cast?)", diff --git a/mcs/mcs/expression.cs b/mcs/mcs/expression.cs index acb03242cfe..a346cc4581b 100644 --- a/mcs/mcs/expression.cs +++ b/mcs/mcs/expression.cs @@ -138,6 +138,9 @@ namespace Mono.CSharp { AddressOf, TOP } + public static readonly string [] oper_names; + static Type [] [] predefined_operators; + public readonly Operator Oper; public Expression Expr; @@ -148,29 +151,6 @@ namespace Mono.CSharp { this.loc = loc; } - /// - /// Returns a stringified representation of the Operator - /// - static public string OperName (Operator oper) - { - switch (oper){ - case Operator.UnaryPlus: - return "+"; - case Operator.UnaryNegation: - return "-"; - case Operator.LogicalNot: - return "!"; - case Operator.OnesComplement: - return "~"; - case Operator.AddressOf: - return "&"; - } - - return oper.ToString (); - } - - public static readonly string [] oper_names; - static Unary () { oper_names = new string [(int)Operator.TOP]; @@ -182,22 +162,6 @@ namespace Mono.CSharp { oper_names [(int) Operator.AddressOf] = "op_AddressOf"; } - public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t) - { - Error_OperatorCannotBeApplied (loc, oper, TypeManager.CSharpName (t)); - } - - public static void Error_OperatorCannotBeApplied (Location loc, string oper, string type) - { - Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'", - oper, type); - } - - void Error23 (Type t) - { - Error_OperatorCannotBeApplied (loc, OperName (Oper), t); - } - // // This routine will attempt to simplify the unary expression when the // argument is a constant. @@ -346,215 +310,45 @@ namespace Mono.CSharp { return e; } return null; - - case Operator.AddressOf: - return e; - } throw new Exception ("Can not constant fold: " + Oper.ToString()); } - Expression ResolveOperator (EmitContext ec) + Expression ResolveOperator (EmitContext ec, Expression expr) { - // - // Step 1: Default operations on CLI native types. - // + if (predefined_operators == null) + CreatePredefinedOperatorsTable (); - // Attempt to use a constant folding operation. - Constant cexpr = Expr as Constant; - if (cexpr != null) { - cexpr = TryReduceConstant (ec, cexpr); - if (cexpr != null) { - return cexpr; - } - } + Type expr_type = expr.Type; + Expression best_expr; // - // Step 2: Perform Operator Overload location + // Primitive types first // - Type expr_type = Expr.Type; - string op_name = oper_names [(int) Oper]; - - MethodGroupExpr user_op = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr; - if (user_op != null) { - ArrayList args = new ArrayList (1); - args.Add (new Argument (Expr)); - user_op = user_op.OverloadResolve (ec, ref args, false, loc); - - if (user_op == null) { - Error23 (expr_type); + if (TypeManager.IsPrimitiveType (expr_type)) { + best_expr = ResolvePrimitivePredefinedType (expr); + if (best_expr == null) return null; - } - - return new UserOperatorCall (user_op, args, CreateExpressionTree, loc); - } - switch (Oper){ - case Operator.LogicalNot: - if (expr_type != TypeManager.bool_type) { - Expr = ResolveBoolean (ec, Expr, loc); - if (Expr == null){ - Error23 (expr_type); - return null; - } - } - - type = TypeManager.bool_type; + type = best_expr.Type; + Expr = best_expr; return this; + } - case Operator.OnesComplement: - // Unary numeric promotions - if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type || - expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type || - expr_type == TypeManager.char_type) - { - type = TypeManager.int32_type; - return EmptyCast.Create (this, type); - } - - // Predefined operators - if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type || - expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type || - TypeManager.IsEnumType (expr_type)) - { - type = expr_type; - return this; - } - - type = TypeManager.int32_type; - Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc); - if (Expr != null) - return this; - - Error23 (expr_type); - return null; - - case Operator.AddressOf: - if (!ec.InUnsafe) { - UnsafeError (loc); - return null; - } - - if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){ - return null; - } - - IVariable variable = Expr as IVariable; - bool is_fixed = variable != null && variable.VerifyFixed (); - - if (!ec.InFixedInitializer && !is_fixed) { - Error (212, "You can only take the address of unfixed expression inside " + - "of a fixed statement initializer"); - return null; - } - - if (ec.InFixedInitializer && is_fixed) { - Error (213, "You cannot use the fixed statement to take the address of an already fixed expression"); - return null; - } - - LocalVariableReference lr = Expr as LocalVariableReference; - if (lr != null){ - if (lr.local_info.IsCaptured){ - AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc); - return null; - } - lr.local_info.AddressTaken = true; - lr.local_info.Used = true; - } - - ParameterReference pr = Expr as ParameterReference; - if ((pr != null) && pr.Parameter.IsCaptured) { - AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc); + // + // E operator ~(E x); + // + if (Oper == Operator.OnesComplement && TypeManager.IsEnumType (expr_type)) { + best_expr = ResolvePrimitivePredefinedType (EmptyCast.Create (expr, TypeManager.GetEnumUnderlyingType (expr_type))); + if (best_expr == null) return null; - } - // According to the specs, a variable is considered definitely assigned if you take - // its address. - if ((variable != null) && (variable.VariableInfo != null)){ - variable.VariableInfo.SetAssigned (ec); - } - - type = TypeManager.GetPointerType (Expr.Type); + Expr = EmptyCast.Create (best_expr, expr_type); + type = Expr.Type; return this; - - case Operator.UnaryPlus: - // Unary numeric promotions - if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type || - expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type || - expr_type == TypeManager.char_type) - { - return EmptyCast.Create (Expr, TypeManager.int32_type); - } - - // Predefined operators - if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type || - expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type || - expr_type == TypeManager.float_type || expr_type == TypeManager.double_type || - expr_type == TypeManager.decimal_type) - { - return Expr; - } - - Expr = Convert.ImplicitUserConversion(ec, Expr, TypeManager.int32_type, loc); - if (Expr != null) { - // Because we can completely ignore unary + - return Expr; - } - - Error23 (expr_type); - return null; - - case Operator.UnaryNegation: - // - // transform - - expr into expr - // - Unary u = Expr as Unary; - if (u != null && u.Oper == Operator.UnaryNegation) { - return u.Expr; - } - - // Unary numeric promotions - if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type || - expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type || - expr_type == TypeManager.char_type) - { - type = TypeManager.int32_type; - return EmptyCast.Create (this, type); - } - - // - // Predefined operators - // - if (expr_type == TypeManager.uint32_type) { - type = TypeManager.int64_type; - Expr = Convert.ImplicitNumericConversion (Expr, type); - return this; - } - - if (expr_type == TypeManager.int32_type || expr_type == TypeManager.int64_type || - expr_type == TypeManager.float_type || expr_type == TypeManager.double_type || - expr_type == TypeManager.decimal_type) - { - type = expr_type; - return this; - } - - // - // User conversion - - type = TypeManager.int32_type; - Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc); - if (Expr != null) - return this; - - Error23 (expr_type); - return null; } - Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" + - TypeManager.CSharpName (expr_type) + "'"); - return null; + return ResolveUserType (ec, expr); } public override Expression CreateExpressionTree (EmitContext ec) @@ -583,8 +377,68 @@ namespace Mono.CSharp { return CreateExpressionFactoryCall (method_name, args); } + static void CreatePredefinedOperatorsTable () + { + predefined_operators = new Type [(int) Operator.TOP] []; + + // + // 7.6.1 Unary plus operator + // + predefined_operators [(int) Operator.UnaryPlus] = new Type [] { + TypeManager.int32_type, TypeManager.uint32_type, + TypeManager.int64_type, TypeManager.uint64_type, + TypeManager.float_type, TypeManager.double_type, + TypeManager.decimal_type + }; + + // + // 7.6.2 Unary minus operator + // + predefined_operators [(int) Operator.UnaryNegation] = new Type [] { + TypeManager.int32_type, + TypeManager.int64_type, + TypeManager.float_type, TypeManager.double_type, + TypeManager.decimal_type + }; + + // + // 7.6.3 Logical negation operator + // + predefined_operators [(int) Operator.LogicalNot] = new Type [] { + TypeManager.bool_type + }; + + // + // 7.6.4 Bitwise complement operator + // + predefined_operators [(int) Operator.OnesComplement] = new Type [] { + TypeManager.int32_type, TypeManager.uint32_type, + TypeManager.int64_type, TypeManager.uint64_type + }; + } + + // + // Unary numeric promotions + // + static Expression DoNumericPromotion (Operator op, Expression expr) + { + Type expr_type = expr.Type; + if ((op == Operator.UnaryPlus || op == Operator.UnaryNegation || op == Operator.OnesComplement) && + expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type || + expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type || + expr_type == TypeManager.char_type) + return Convert.ImplicitNumericConversion (expr, TypeManager.int32_type); + + if (op == Operator.UnaryNegation && expr_type == TypeManager.uint32_type) + return Convert.ImplicitNumericConversion (expr, TypeManager.int64_type); + + return expr; + } + public override Expression DoResolve (EmitContext ec) { + eclass = ExprClass.Value; + if (Oper == Operator.AddressOf) { Expr = Expr.DoResolveLValue (ec, new EmptyExpression ()); @@ -592,10 +446,11 @@ namespace Mono.CSharp { Error (211, "Cannot take the address of the given expression"); return null; } + + return ResolveAddressOf (ec); } - else - Expr = Expr.Resolve (ec); + Expr = Expr.Resolve (ec); if (Expr == null) return null; @@ -604,8 +459,27 @@ namespace Mono.CSharp { return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec); #endif - eclass = ExprClass.Value; - return ResolveOperator (ec); + // + // Attempt to use a constant folding operation. + // + Constant cexpr = Expr as Constant; + if (cexpr != null) { + cexpr = TryReduceConstant (ec, cexpr); + if (cexpr != null) + return cexpr; + } + + Expression expr = ResolveOperator (ec, Expr); + if (expr == null) + Error_OperatorCannotBeApplied (loc, OperName (Oper), Expr.Type); + + // + // Reduce unary operator on predefined types + // + if (expr == this && Oper == Operator.UnaryPlus) + return Expr; + + return expr; } public override Expression DoResolveLValue (EmitContext ec, Expression right) @@ -664,9 +538,163 @@ namespace Mono.CSharp { base.EmitBranchable (ec, target, on_true); } - public override string ToString () + public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t) + { + Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'", + oper, TypeManager.CSharpName (t)); + } + + // + // Returns a stringified representation of the Operator + // + public static string OperName (Operator oper) + { + switch (oper) { + case Operator.UnaryPlus: + return "+"; + case Operator.UnaryNegation: + return "-"; + case Operator.LogicalNot: + return "!"; + case Operator.OnesComplement: + return "~"; + case Operator.AddressOf: + return "&"; + } + + throw new NotImplementedException (oper.ToString ()); + } + + Expression ResolveAddressOf (EmitContext ec) + { + if (!ec.InUnsafe) { + UnsafeError (loc); + return null; + } + + if (!TypeManager.VerifyUnManaged (Expr.Type, loc)) { + return null; + } + + IVariable variable = Expr as IVariable; + bool is_fixed = variable != null && variable.VerifyFixed (); + + if (!ec.InFixedInitializer && !is_fixed) { + Error (212, "You can only take the address of unfixed expression inside " + + "of a fixed statement initializer"); + return null; + } + + if (ec.InFixedInitializer && is_fixed) { + Error (213, "You cannot use the fixed statement to take the address of an already fixed expression"); + return null; + } + + LocalVariableReference lr = Expr as LocalVariableReference; + if (lr != null) { + if (lr.local_info.IsCaptured) { + AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc); + return null; + } + lr.local_info.AddressTaken = true; + lr.local_info.Used = true; + } + + ParameterReference pr = Expr as ParameterReference; + if ((pr != null) && pr.Parameter.IsCaptured) { + AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc); + return null; + } + + // According to the specs, a variable is considered definitely assigned if you take + // its address. + if ((variable != null) && (variable.VariableInfo != null)) { + variable.VariableInfo.SetAssigned (ec); + } + + type = TypeManager.GetPointerType (Expr.Type); + return this; + } + + Expression ResolvePrimitivePredefinedType (Expression expr) { - return "Unary (" + Oper + ", " + Expr + ")"; + expr = DoNumericPromotion (Oper, expr); + Type expr_type = expr.Type; + Type[] predefined = predefined_operators [(int) Oper]; + foreach (Type t in predefined) { + if (t == expr_type) + return expr; + } + return null; + } + + // + // Unary user type overload resolution + // + Expression ResolveUserType (EmitContext ec, Expression expr) + { + // + // Perform user-operator overload resolution + // + string op_name = oper_names [(int) Oper]; + MethodGroupExpr user_op = MemberLookup (ec.ContainerType, expr.Type, op_name, MemberTypes.Method, AllBindingFlags, expr.Location) as MethodGroupExpr; + if (user_op != null) { + ArrayList args = new ArrayList (1); + args.Add (new Argument (expr)); + user_op = user_op.OverloadResolve (ec, ref args, false, expr.Location); + + if (user_op != null) { + Expr = ((Argument) args [0]).Expr; + return new UserOperatorCall (user_op, args, CreateExpressionTree, expr.Location); + } + } + + Type[] predefined = predefined_operators [(int) Oper]; + Expression best_expr = null; + foreach (Type t in predefined) { + Expression oper_expr = Convert.UserDefinedConversion (ec, expr, t, expr.Location, false); + if (oper_expr == null) + continue; + + // + // decimal type is predefined but has user-operators + // + if (oper_expr.Type == TypeManager.decimal_type) + oper_expr = ResolveUserType (ec, oper_expr); + else + oper_expr = ResolvePrimitivePredefinedType (oper_expr); + + if (oper_expr == null) + continue; + + if (best_expr == null) { + best_expr = oper_expr; + continue; + } + + int result = MethodGroupExpr.BetterTypeConversion (ec, best_expr.Type, t); + if (result == 0) { + Report.Error (35, loc, "Operator `{0}' is ambiguous on an operand of type `{1}'", + OperName (Oper), TypeManager.CSharpName (expr.Type)); + break; + } + + if (result == 2) + best_expr = oper_expr; + } + + if (best_expr == null) + return null; + + // + // HACK: Decimal user-operator is included in standard operators + // + if (best_expr.Type == TypeManager.decimal_type) + return best_expr; + + Expr = best_expr; + type = best_expr.Type; + return this; } protected override void CloneTo (CloneContext clonectx, Expression t) @@ -911,6 +939,11 @@ namespace Mono.CSharp { return this; } + public override Expression CreateExpressionTree (EmitContext ec) + { + return new Assign (this, this).CreateExpressionTree (ec); + } + public override Expression DoResolve (EmitContext ec) { expr = expr.Resolve (ec); @@ -6980,14 +7013,11 @@ namespace Mono.CSharp { } Type expr_type = expr_resolved.Type; - if (expr_type.IsPointer || expr_type == TypeManager.void_type || expr_resolved is NullLiteral){ + if (expr_type.IsPointer || expr_type == TypeManager.void_type || + expr_resolved is NullLiteral || expr_type == TypeManager.anonymous_method_type) { Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type); return null; } - if (expr_type == TypeManager.anonymous_method_type){ - Unary.Error_OperatorCannotBeApplied (loc, ".", "anonymous method"); - return null; - } Constant c = expr_resolved as Constant; if (c != null && c.GetValue () == null) { diff --git a/mcs/mcs/nullable.cs b/mcs/mcs/nullable.cs index 95eb146c8ef..185dcf46b42 100644 --- a/mcs/mcs/nullable.cs +++ b/mcs/mcs/nullable.cs @@ -467,7 +467,10 @@ namespace Mono.CSharp.Nullable Type type = TypeManager.GetTypeArguments (TargetType) [0]; if (IsUser) { - return Convert.UserDefinedConversion (ec, unwrap, type, loc, IsExplicit); + if (IsExplicit) + return Convert.ExplicitUserConversion (ec, unwrap, type, loc); + else + return Convert.ImplicitUserConversion (ec, unwrap, type, loc); } else { if (IsExplicit) return Convert.ExplicitConversion (ec, unwrap, type, loc); -- 2.25.1