Merge pull request #1851 from esdrubal/mmf
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / ConditionalExpression.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
17 using System.Dynamic.Utils;
18 using System.Diagnostics;
19
20 #if !FEATURE_CORE_DLR
21 namespace Microsoft.Scripting.Ast {
22 #else
23 namespace System.Linq.Expressions {
24 #endif
25
26     /// <summary>
27     /// Represents an expression that has a conditional operator.
28     /// </summary>
29     [DebuggerTypeProxy(typeof(Expression.ConditionalExpressionProxy))]
30     public class ConditionalExpression : Expression {
31         private readonly Expression _test;
32         private readonly Expression _true;
33
34         internal ConditionalExpression(Expression test, Expression ifTrue) {
35             _test = test;
36             _true = ifTrue;
37         }
38
39         internal static ConditionalExpression Make(Expression test, Expression ifTrue, Expression ifFalse, Type type) {
40             if (ifTrue.Type != type || ifFalse.Type != type) {
41                 return new FullConditionalExpressionWithType(test, ifTrue, ifFalse, type);
42             } if (ifFalse is DefaultExpression && ifFalse.Type == typeof(void)) {
43                 return new ConditionalExpression(test, ifTrue);
44             } else {
45                 return new FullConditionalExpression(test, ifTrue, ifFalse);
46             }
47         }
48
49         /// <summary>
50         /// Returns the node type of this Expression. Extension nodes should return
51         /// ExpressionType.Extension when overriding this method.
52         /// </summary>
53         /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
54         public sealed override ExpressionType NodeType {
55             get { return ExpressionType.Conditional; }
56         }
57
58         /// <summary>
59         /// Gets the static type of the expression that this <see cref="Expression" /> represents.
60         /// </summary>
61         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
62         public override Type Type {
63             get { return IfTrue.Type; }
64         }
65
66         /// <summary>
67         /// Gets the test of the conditional operation.
68         /// </summary>
69         public Expression Test {
70             get { return _test; }
71         }
72         /// <summary>
73         /// Gets the expression to execute if the test evaluates to true.
74         /// </summary>
75         public Expression IfTrue {
76             get { return _true; }
77         }
78         /// <summary>
79         /// Gets the expression to execute if the test evaluates to false.
80         /// </summary>
81         public Expression IfFalse {
82             get { return GetFalse(); }
83         }
84
85         internal virtual Expression GetFalse() {
86             return Expression.Empty();
87         }
88
89         /// <summary>
90         /// Dispatches to the specific visit method for this node type.
91         /// </summary>
92         protected internal override Expression Accept(ExpressionVisitor visitor) {
93             return visitor.VisitConditional(this);
94         }
95
96         /// <summary>
97         /// Creates a new expression that is like this one, but using the
98         /// supplied children. If all of the children are the same, it will
99         /// return this expression.
100         /// </summary>
101         /// <param name="test">The <see cref="Test" /> property of the result.</param>
102         /// <param name="ifTrue">The <see cref="IfTrue" /> property of the result.</param>
103         /// <param name="ifFalse">The <see cref="IfFalse" /> property of the result.</param>
104         /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
105         public ConditionalExpression Update(Expression test, Expression ifTrue, Expression ifFalse) {
106             if (test == Test && ifTrue == IfTrue && ifFalse == IfFalse) {
107                 return this;
108             }
109             return Expression.Condition(test, ifTrue, ifFalse, Type);
110         }
111     }
112
113     internal class FullConditionalExpression : ConditionalExpression {
114         private readonly Expression _false;
115
116         internal FullConditionalExpression(Expression test, Expression ifTrue, Expression ifFalse)
117             : base(test, ifTrue) {
118             _false = ifFalse;
119         }
120
121         internal override Expression GetFalse() {
122             return _false;
123         }
124     }
125
126     internal class FullConditionalExpressionWithType : FullConditionalExpression {
127         private readonly Type _type;
128
129         internal FullConditionalExpressionWithType(Expression test, Expression ifTrue, Expression ifFalse, Type type)
130             : base(test, ifTrue, ifFalse) {
131             _type = type;
132         }
133
134         public sealed override Type Type {
135             get { return _type; }
136         }
137     }
138
139     public partial class Expression {
140
141         /// <summary>
142         /// Creates a <see cref="ConditionalExpression"/>.
143         /// </summary>
144         /// <param name="test">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.Test"/> property equal to.</param>
145         /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfTrue"/> property equal to.</param>
146         /// <param name="ifFalse">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfFalse"/> property equal to.</param>
147         /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to 
148         /// <see cref="F:ExpressionType.Conditional"/> and the <see cref="P:ConditionalExpression.Test"/>, <see cref="P:ConditionalExpression.IfTrue"/>, 
149         /// and <see cref="P:ConditionalExpression.IfFalse"/> properties set to the specified values.</returns>
150         public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse) {
151             RequiresCanRead(test, "test");
152             RequiresCanRead(ifTrue, "ifTrue");
153             RequiresCanRead(ifFalse, "ifFalse");
154
155             if (test.Type != typeof(bool)) {
156                 throw Error.ArgumentMustBeBoolean();
157             }
158             if (!TypeUtils.AreEquivalent(ifTrue.Type, ifFalse.Type)) {
159                 throw Error.ArgumentTypesMustMatch();
160             }
161
162             return ConditionalExpression.Make(test, ifTrue, ifFalse, ifTrue.Type);
163         }
164
165
166         /// <summary>
167         /// Creates a <see cref="ConditionalExpression"/>.
168         /// </summary>
169         /// <param name="test">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.Test"/> property equal to.</param>
170         /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfTrue"/> property equal to.</param>
171         /// <param name="ifFalse">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfFalse"/> property equal to.</param>
172         /// <param name="type">A <see cref="Type"/> to set the <see cref="P:Expression.Type"/> property equal to.</param>
173         /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to 
174         /// <see cref="F:ExpressionType.Conditional"/> and the <see cref="P:ConditionalExpression.Test"/>, <see cref="P:ConditionalExpression.IfTrue"/>, 
175         /// and <see cref="P:ConditionalExpression.IfFalse"/> properties set to the specified values.</returns>
176         /// <remarks>This method allows explicitly unifying the result type of the conditional expression in cases where the types of <paramref name="ifTrue"/>
177         /// and <paramref name="ifFalse"/> expressions are not equal. Types of both <paramref name="ifTrue"/> and <paramref name="ifFalse"/> must be implicitly
178         /// reference assignable to the result type. The <paramref name="type"/> is allowed to be <see cref="System.Void"/>.</remarks>
179         public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse, Type type) {
180             RequiresCanRead(test, "test");
181             RequiresCanRead(ifTrue, "ifTrue");
182             RequiresCanRead(ifFalse, "ifFalse");
183             ContractUtils.RequiresNotNull(type, "type");
184
185             if (test.Type != typeof(bool)) {
186                 throw Error.ArgumentMustBeBoolean();
187             }
188
189             if (type != typeof(void)) {
190                 if (!TypeUtils.AreReferenceAssignable(type, ifTrue.Type) ||
191                     !TypeUtils.AreReferenceAssignable(type, ifFalse.Type)) {
192                     throw Error.ArgumentTypesMustMatch();
193                 }
194             }
195
196             return ConditionalExpression.Make(test, ifTrue, ifFalse, type);
197         }
198
199         /// <summary>
200         /// Creates a <see cref="ConditionalExpression"/>.
201         /// </summary>
202         /// <param name="test">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.Test"/> property equal to.</param>
203         /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfTrue"/> property equal to.</param>
204         /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to 
205         /// <see cref="F:ExpressionType.Conditional"/> and the <see cref="P:ConditionalExpression.Test"/>, <see cref="P:ConditionalExpression.IfTrue"/>, 
206         /// properties set to the specified values. The <see cref="P:ConditionalExpression.IfFalse"/> property is set to default expression and
207         /// the type of the resulting <see cref="ConditionalExpression"/> returned by this method is <see cref="System.Void"/>.</returns>
208         public static ConditionalExpression IfThen(Expression test, Expression ifTrue) {
209             return Condition(test, ifTrue, Expression.Empty(), typeof(void));
210         }
211
212         /// <summary>
213         /// Creates a <see cref="ConditionalExpression"/>.
214         /// </summary>
215         /// <param name="test">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.Test"/> property equal to.</param>
216         /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfTrue"/> property equal to.</param>
217         /// <param name="ifFalse">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfFalse"/> property equal to.</param>
218         /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to 
219         /// <see cref="F:ExpressionType.Conditional"/> and the <see cref="P:ConditionalExpression.Test"/>, <see cref="P:ConditionalExpression.IfTrue"/>, 
220         /// and <see cref="P:ConditionalExpression.IfFalse"/> properties set to the specified values. The type of the resulting <see cref="ConditionalExpression"/>
221         /// returned by this method is <see cref="System.Void"/>.</returns>
222         public static ConditionalExpression IfThenElse(Expression test, Expression ifTrue, Expression ifFalse) {
223             return Condition(test, ifTrue, ifFalse, typeof(void));
224         }
225     }
226 }