2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[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 Microsoft Public License. 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  Microsoft Public License, 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 Microsoft Public License.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 #if CODEPLEX_40
19 using System.Dynamic.Utils;
20 #else
21 using Microsoft.Scripting.Utils;
22 #endif
23 using System.Diagnostics;
24
25 #if CODEPLEX_40
26 namespace System.Linq.Expressions {
27 #else
28 namespace Microsoft.Linq.Expressions {
29 #endif
30
31     /// <summary>
32     /// Represents an expression that has a conditional operator.
33     /// </summary>
34 #if !SILVERLIGHT
35     [DebuggerTypeProxy(typeof(Expression.ConditionalExpressionProxy))]
36 #endif
37     public class ConditionalExpression : Expression {
38         private readonly Expression _test;
39         private readonly Expression _true;
40
41         internal ConditionalExpression(Expression test, Expression ifTrue) {
42             _test = test;
43             _true = ifTrue;
44         }
45
46         internal static ConditionalExpression Make(Expression test, Expression ifTrue, Expression ifFalse, Type type) {
47             if (ifTrue.Type != type || ifFalse.Type != type) {
48                 return new FullConditionalExpressionWithType(test, ifTrue, ifFalse, type);
49             } if (ifFalse is DefaultExpression && ifFalse.Type == typeof(void)) {
50                 return new ConditionalExpression(test, ifTrue);
51             } else {
52                 return new FullConditionalExpression(test, ifTrue, ifFalse);
53             }
54         }
55
56         /// <summary>
57         /// Returns the node type of this Expression. Extension nodes should return
58         /// ExpressionType.Extension when overriding this method.
59         /// </summary>
60         /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
61         public sealed override ExpressionType NodeType {
62             get { return ExpressionType.Conditional; }
63         }
64
65         /// <summary>
66         /// Gets the static type of the expression that this <see cref="Expression" /> represents.
67         /// </summary>
68         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
69         public override Type Type {
70             get { return IfTrue.Type; }
71         }
72
73         /// <summary>
74         /// Gets the test of the conditional operation.
75         /// </summary>
76         public Expression Test {
77             get { return _test; }
78         }
79         /// <summary>
80         /// Gets the expression to execute if the test evaluates to true.
81         /// </summary>
82         public Expression IfTrue {
83             get { return _true; }
84         }
85         /// <summary>
86         /// Gets the expression to execute if the test evaluates to false.
87         /// </summary>
88         public Expression IfFalse {
89             get { return GetFalse(); }
90         }
91
92         internal virtual Expression GetFalse() {
93             return Expression.Empty();
94         }
95
96         internal override Expression Accept(ExpressionVisitor visitor) {
97             return visitor.VisitConditional(this);
98         }
99     }
100
101     internal class FullConditionalExpression : ConditionalExpression {
102         private readonly Expression _false;
103
104         internal FullConditionalExpression(Expression test, Expression ifTrue, Expression ifFalse)
105             : base(test, ifTrue) {
106             _false = ifFalse;
107         }
108
109         internal override Expression GetFalse() {
110             return _false;
111         }
112     }
113
114     internal class FullConditionalExpressionWithType : FullConditionalExpression {
115         private readonly Type _type;
116
117         internal FullConditionalExpressionWithType(Expression test, Expression ifTrue, Expression ifFalse, Type type)
118             : base(test, ifTrue, ifFalse) {
119             _type = type;
120         }
121
122         public sealed override Type Type {
123             get { return _type; }
124         }
125     }
126
127     public partial class Expression {
128
129         /// <summary>
130         /// Creates a <see cref="ConditionalExpression"/>.
131         /// </summary>
132         /// <param name="test">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.Test"/> property equal to.</param>
133         /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfTrue"/> property equal to.</param>
134         /// <param name="ifFalse">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfFalse"/> property equal to.</param>
135         /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to 
136         /// <see cref="F:ExpressionType.Conditional"/> and the <see cref="P:ConditionalExpression.Test"/>, <see cref="P:ConditionalExpression.IfTrue"/>, 
137         /// and <see cref="P:ConditionalExpression.IfFalse"/> properties set to the specified values.</returns>
138         public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse) {
139             RequiresCanRead(test, "test");
140             RequiresCanRead(ifTrue, "ifTrue");
141             RequiresCanRead(ifFalse, "ifFalse");
142
143             if (test.Type != typeof(bool)) {
144                 throw Error.ArgumentMustBeBoolean();
145             }
146             if (!TypeUtils.AreEquivalent(ifTrue.Type, ifFalse.Type)) {
147                 throw Error.ArgumentTypesMustMatch();
148             }
149
150             return ConditionalExpression.Make(test, ifTrue, ifFalse, ifTrue.Type);
151         }
152
153
154         /// <summary>
155         /// Creates a <see cref="ConditionalExpression"/>.
156         /// </summary>
157         /// <param name="test">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.Test"/> property equal to.</param>
158         /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfTrue"/> property equal to.</param>
159         /// <param name="ifFalse">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfFalse"/> property equal to.</param>
160         /// <param name="type">A <see cref="Type"/> to set the <see cref="P:Expression.Type"/> property equal to.</param>
161         /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to 
162         /// <see cref="F:ExpressionType.Conditional"/> and the <see cref="P:ConditionalExpression.Test"/>, <see cref="P:ConditionalExpression.IfTrue"/>, 
163         /// and <see cref="P:ConditionalExpression.IfFalse"/> properties set to the specified values.</returns>
164         /// <remarks>This method allows explicitly unifying the result type of the conditional expression in cases where the types of <paramref name="ifTrue"/>
165         /// and <paramref name="ifFalse"/> expressions are not equal. Types of both <paramref name="ifTrue"/> and <paramref name="ifFalse"/> must be implicitly
166         /// reference assignable to the result type. The <paramref name="type"/> is allowed to be <see cref="System.Void"/>.</remarks>
167         public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse, Type type) {
168             RequiresCanRead(test, "test");
169             RequiresCanRead(ifTrue, "ifTrue");
170             RequiresCanRead(ifFalse, "ifFalse");
171             ContractUtils.RequiresNotNull(type, "type");
172
173             if (test.Type != typeof(bool)) {
174                 throw Error.ArgumentMustBeBoolean();
175             }
176
177             if (type != typeof(void)) {
178                 if (!TypeUtils.AreReferenceAssignable(type, ifTrue.Type) ||
179                     !TypeUtils.AreReferenceAssignable(type, ifFalse.Type)) {
180                     throw Error.ArgumentTypesMustMatch();
181                 }
182             }
183
184             return ConditionalExpression.Make(test, ifTrue, ifFalse, type);
185         }
186
187         /// <summary>
188         /// Creates a <see cref="ConditionalExpression"/>.
189         /// </summary>
190         /// <param name="test">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.Test"/> property equal to.</param>
191         /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfTrue"/> property equal to.</param>
192         /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to 
193         /// <see cref="F:ExpressionType.Conditional"/> and the <see cref="P:ConditionalExpression.Test"/>, <see cref="P:ConditionalExpression.IfTrue"/>, 
194         /// properties set to the specified values. The <see cref="P:ConditionalExpression.IfFalse"/> property is set to default expression and
195         /// the type of the resulting <see cref="ConditionalExpression"/> returned by this method is <see cref="System.Void"/>.</returns>
196         public static ConditionalExpression IfThen(Expression test, Expression ifTrue) {
197             return Condition(test, ifTrue, Expression.Empty(), typeof(void));
198         }
199
200         /// <summary>
201         /// Creates a <see cref="ConditionalExpression"/>.
202         /// </summary>
203         /// <param name="test">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.Test"/> property equal to.</param>
204         /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfTrue"/> property equal to.</param>
205         /// <param name="ifFalse">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfFalse"/> property equal to.</param>
206         /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to 
207         /// <see cref="F:ExpressionType.Conditional"/> and the <see cref="P:ConditionalExpression.Test"/>, <see cref="P:ConditionalExpression.IfTrue"/>, 
208         /// and <see cref="P:ConditionalExpression.IfFalse"/> properties set to the specified values. The type of the resulting <see cref="ConditionalExpression"/>
209         /// returned by this method is <see cref="System.Void"/>.</returns>
210         public static ConditionalExpression IfThenElse(Expression test, Expression ifTrue, Expression ifFalse) {
211             return Condition(test, ifTrue, ifFalse, typeof(void));
212         }
213     }
214 }