Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit cfcef49

Browse files
JonHannaOmarTawfik
authored andcommitted
Microsoft.CSharp: Set temporary unchecked context correctly. (#25825)
* Set temporary unchecked context correctly. Fixes #25824 Fix also makes ConstOutOfRange and ConstOutOfRangeChecked reachable (were made erroneously unreachable by #25824), so contributes to #22470 * Don't optimise out casts in enum unary operations. Fixes #25826
1 parent cb13ef7 commit cfcef49

File tree

6 files changed

+384
-4
lines changed

6 files changed

+384
-4
lines changed

src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BindingContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public BindingContext(CSemanticChecker semanticChecker, ExprFactory exprFactory)
2424
public BindingContext(BindingContext parent)
2525
: this(parent.SemanticChecker, parent.ExprFactory)
2626
{
27+
// We copy the context object, but leave checking false.
2728
ContextForMemberLookup = parent.ContextForMemberLookup;
28-
Checked = parent.Checked;
2929
}
3030

3131
//The SymbolLoader can be retrieved from SemanticChecker,

src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,13 @@ public Expr BindStandardUnaryOperator(OperatorKind op, Expr pArgument)
12511251
return BindLiftedStandardUnop(ek, flags, pArgument, uofs);
12521252
}
12531253

1254+
if (pArgument.isCONSTANT_OK())
1255+
{
1256+
// Wrap the constant in an identity cast, to force the later casts to not be optimised out.
1257+
// The ExpressionTreeRewriter will remove this again.
1258+
pArgument = ExprFactory.CreateCast(pArgument.Type, pArgument);
1259+
}
1260+
12541261
// Try the conversion - if it fails, do a cast without user defined casts.
12551262
Expr arg = tryConvert(pArgument, uofs.GetType());
12561263
if (arg == null)
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Runtime.CompilerServices;
9+
using Xunit;
10+
11+
namespace Microsoft.CSharp.RuntimeBinder.Tests
12+
{
13+
public class AssignmentTests
14+
{
15+
public static object[][] Additions =
16+
{
17+
new object[]{1, 2, 3},
18+
new object[]{1L, 2, 3L},
19+
new object[]{1, 2L, 3L},
20+
new object[]{1U, 2U, 3U},
21+
new object[]{1, 2U, 3L},
22+
new object[]{1, 2M, 3M},
23+
new object[]{1.0, 2, 3.0},
24+
new object[]{1, 2.0, 3.0}
25+
};
26+
27+
private sealed class HasProperty<T>
28+
{
29+
public T Prop { get; set; }
30+
}
31+
32+
private static HasProperty<T> GetHasProperty<T>(T initial) => new HasProperty<T>{ Prop = initial };
33+
34+
public static object[][] PropertyAdditions =
35+
{
36+
new object[]{GetHasProperty(1), 2, 3},
37+
new object[]{GetHasProperty(1L), 2, 3L},
38+
new object[]{GetHasProperty(1), 2L, 3},
39+
new object[]{GetHasProperty(1U), 2U, 3U},
40+
new object[]{GetHasProperty(1), 2U, 3},
41+
new object[]{GetHasProperty(1), 2M, 3},
42+
new object[]{GetHasProperty(1.0), 2, 3.0},
43+
new object[]{GetHasProperty(1), 2.0, 3}
44+
};
45+
46+
public static object[][] PropertyAssigments =
47+
{
48+
new object[]{GetHasProperty(1), 2, 2},
49+
new object[]{GetHasProperty(1L), 2, 2L},
50+
new object[]{GetHasProperty(1), 2L, 2},
51+
new object[]{GetHasProperty(1U), 2U, 2U},
52+
new object[]{GetHasProperty(1), 2U, 2},
53+
new object[]{GetHasProperty(1), 2M, 2},
54+
new object[]{GetHasProperty(1.0), 2, 2.0},
55+
new object[]{GetHasProperty(1), 2.0, 2}
56+
};
57+
58+
public static object[][] PropertyBadAssigments =
59+
{
60+
new object[]{GetHasProperty(1), decimal.MaxValue, true, false},
61+
new object[]{GetHasProperty(1), DateTime.MinValue, false, false},
62+
new object[]{GetHasProperty(2), "hello", false, false},
63+
new object[]{GetHasProperty(1), null, false, false},
64+
new object[]{GetHasProperty(true), 2, false, false},
65+
new object[]{GetHasProperty(2.0), false, false, false},
66+
new object[]{GetHasProperty("abc"), 2.0, false, false},
67+
new object[]{GetHasProperty("abc"), new object(), false, true},
68+
new object[]{GetHasProperty(2), new object(), false, true}
69+
};
70+
71+
public static IEnumerable<object[]> PropertyBadCheckedAssigments =
72+
new[]
73+
{
74+
new object[] {GetHasProperty(1), long.MaxValue, true, false},
75+
new object[] {GetHasProperty(1), (double)long.MaxValue, true, false},
76+
new object[] {GetHasProperty(1U), -1, true, false},
77+
new object[] {GetHasProperty(1), uint.MaxValue, true, false},
78+
new object[] {GetHasProperty(default(short)), int.MaxValue, true, false},
79+
new object[]{GetHasProperty(2UL), -1, true, false}
80+
}.Concat(PropertyBadAssigments);
81+
82+
[Theory, MemberData(nameof(Additions))]
83+
public void AddAssignment(dynamic lhs, dynamic rhs, object expected)
84+
{
85+
lhs += rhs;
86+
Assert.Equal(expected, lhs);
87+
Assert.IsType(expected.GetType(), lhs);
88+
}
89+
90+
[Theory, MemberData(nameof(PropertyAdditions))]
91+
public void PropertyAddAssignment(dynamic lhs, dynamic rhs, object expected)
92+
{
93+
lhs.Prop += rhs;
94+
Assert.Equal(expected, lhs.Prop);
95+
}
96+
97+
[Theory, MemberData(nameof(PropertyAssigments))]
98+
public void PropertyAddResultAssigment(object lhs, object rhs, object expected)
99+
{
100+
CallSite<Func<CallSite, object, object, object>> callSite =
101+
CallSite<Func<CallSite, object, object, object>>.Create(
102+
Binder.SetMember(
103+
CSharpBinderFlags.ValueFromCompoundAssignment, "Prop", GetType(),
104+
new[]
105+
{
106+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
107+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
108+
}));
109+
object result = callSite.Target(callSite, lhs, rhs);
110+
Assert.Equal(expected, result);
111+
Assert.Equal(expected, ((dynamic)lhs).Prop);
112+
}
113+
114+
[Theory, MemberData(nameof(PropertyAssigments))]
115+
public void PropertyAddConstantResultAssigment(object lhs, object rhs, object expected)
116+
{
117+
CallSite<Func<CallSite, object, object, object>> callSite =
118+
CallSite<Func<CallSite, object, object, object>>.Create(
119+
Binder.SetMember(
120+
CSharpBinderFlags.ValueFromCompoundAssignment, "Prop", GetType(),
121+
new[]
122+
{
123+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
124+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null)
125+
}));
126+
object result = callSite.Target(callSite, lhs, rhs);
127+
Assert.Equal(expected, result);
128+
Assert.Equal(expected, ((dynamic)lhs).Prop);
129+
}
130+
131+
[Theory, MemberData(nameof(PropertyBadAssigments))]
132+
public void PropertyIncompatibleAssigment(object lhs, object rhs, bool overflow, bool invalidCast)
133+
{
134+
CallSite<Func<CallSite, object, object, object>> callSite =
135+
CallSite<Func<CallSite, object, object, object>>.Create(
136+
Binder.SetMember(
137+
CSharpBinderFlags.ValueFromCompoundAssignment, "Prop", GetType(),
138+
new[]
139+
{
140+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
141+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
142+
}));
143+
if (invalidCast) // Invalid cast at runtime
144+
{
145+
Assert.Throws<InvalidCastException>(() => callSite.Target(callSite, lhs, rhs));
146+
}
147+
else if (overflow) // Overflows at runtime, rather than fail at bind time
148+
{
149+
Assert.Throws<OverflowException>(() => callSite.Target(callSite, lhs, rhs));
150+
}
151+
else
152+
{
153+
Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, lhs, rhs));
154+
}
155+
}
156+
157+
[Theory, MemberData(nameof(PropertyBadAssigments))]
158+
public void PropertyIncompatibleConstantAssigment(object lhs, object rhs, bool _, bool invalidCast)
159+
{
160+
CallSite<Func<CallSite, object, object, object>> callSite =
161+
CallSite<Func<CallSite, object, object, object>>.Create(
162+
Binder.SetMember(
163+
CSharpBinderFlags.ValueFromCompoundAssignment, "Prop", GetType(),
164+
new[]
165+
{
166+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
167+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null)
168+
}));
169+
if (invalidCast)
170+
{
171+
Assert.Throws<InvalidCastException>(() => callSite.Target(callSite, lhs, rhs));
172+
}
173+
else
174+
{
175+
Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, lhs, rhs));
176+
}
177+
}
178+
179+
[Theory, MemberData(nameof(PropertyBadCheckedAssigments))]
180+
public void PropertyIncompatibleCheckedAssigment(object lhs, object rhs, bool overflow, bool invalidCast)
181+
{
182+
CallSite<Func<CallSite, object, object, object>> callSite =
183+
CallSite<Func<CallSite, object, object, object>>.Create(
184+
Binder.SetMember(
185+
CSharpBinderFlags.ValueFromCompoundAssignment | CSharpBinderFlags.CheckedContext, "Prop",
186+
GetType(),
187+
new[]
188+
{
189+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
190+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
191+
}));
192+
if (invalidCast)
193+
{
194+
Assert.Throws<InvalidCastException>(() => callSite.Target(callSite, lhs, rhs));
195+
}
196+
else if (overflow) // Overflows at runtime, rather than fail at bind time
197+
{
198+
Assert.Throws<OverflowException>(() => callSite.Target(callSite, lhs, rhs));
199+
}
200+
else
201+
{
202+
Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, lhs, rhs));
203+
}
204+
}
205+
206+
[Theory, MemberData(nameof(PropertyBadCheckedAssigments))]
207+
public void PropertyIncompatibleConstantCheckedAssigment(object lhs, object rhs, bool _, bool invalidCast)
208+
{
209+
CallSite<Func<CallSite, object, object, object>> callSite =
210+
CallSite<Func<CallSite, object, object, object>>.Create(
211+
Binder.SetMember(
212+
CSharpBinderFlags.ValueFromCompoundAssignment | CSharpBinderFlags.CheckedContext, "Prop",
213+
GetType(),
214+
new[]
215+
{
216+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
217+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null)
218+
}));
219+
if (invalidCast) // Invalid cast at runtime
220+
{
221+
Assert.Throws<InvalidCastException>(() => callSite.Target(callSite, lhs, rhs));
222+
}
223+
else
224+
{
225+
Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, lhs, rhs));
226+
}
227+
}
228+
}
229+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Linq.Expressions;
9+
using System.Runtime.CompilerServices;
10+
using Xunit;
11+
12+
namespace Microsoft.CSharp.RuntimeBinder.Tests
13+
{
14+
public class EnumUnaryOperationTests
15+
{
16+
public enum ByteEnum : byte
17+
{
18+
A,
19+
B,
20+
C,
21+
D,
22+
E
23+
}
24+
25+
public enum SByteEnum : sbyte
26+
{
27+
A,
28+
B,
29+
C,
30+
D,
31+
E,
32+
Z = -1
33+
}
34+
35+
public enum Int32Enum
36+
{
37+
A,
38+
B,
39+
C,
40+
D,
41+
E,
42+
Z = -1
43+
}
44+
45+
public enum UInt32Enum : uint
46+
{
47+
A,
48+
B,
49+
C,
50+
D,
51+
E
52+
}
53+
54+
public enum Int64Enum : long
55+
{
56+
A,
57+
B,
58+
C,
59+
D,
60+
E,
61+
Z = -1
62+
}
63+
64+
public static IEnumerable<object[]> ByteEnums() => Enum.GetValues(typeof(ByteEnum))
65+
.Cast<ByteEnum>()
66+
.Select(x => new object[] { x, ~x });
67+
68+
public static IEnumerable<object[]> SByteEnums() => Enum.GetValues(typeof(SByteEnum))
69+
.Cast<ByteEnum>()
70+
.Select(x => new object[] { x, ~x });
71+
72+
public static IEnumerable<object[]> Int32Enums() => Enum.GetValues(typeof(Int32Enum))
73+
.Cast<Int32Enum>()
74+
.Select(x => new object[] { x, ~x });
75+
76+
public static IEnumerable<object[]> UInt32Enums() => Enum.GetValues(typeof(UInt32Enum))
77+
.Cast<UInt32Enum>()
78+
.Select(x => new object[] { x, ~x });
79+
80+
public static IEnumerable<object[]> Int64Enums() => Enum.GetValues(typeof(Int64Enum))
81+
.Cast<Int64Enum>()
82+
.Select(x => new object[] { x, ~x });
83+
84+
[Theory]
85+
[MemberData(nameof(ByteEnums))]
86+
[MemberData(nameof(SByteEnums))]
87+
[MemberData(nameof(Int32Enums))]
88+
[MemberData(nameof(UInt32Enums))]
89+
[MemberData(nameof(Int64Enums))]
90+
public void EnumOnesComplement(dynamic operand, dynamic result)
91+
{
92+
unchecked
93+
{
94+
Assert.Equal(result, ~operand);
95+
}
96+
}
97+
98+
[Theory]
99+
[MemberData(nameof(ByteEnums))]
100+
[MemberData(nameof(SByteEnums))]
101+
[MemberData(nameof(Int32Enums))]
102+
[MemberData(nameof(UInt32Enums))]
103+
[MemberData(nameof(Int64Enums))]
104+
public void CheckedEnumOnesComplement(dynamic operand, dynamic result)
105+
{
106+
checked
107+
{
108+
Assert.Equal(result, ~operand);
109+
}
110+
}
111+
112+
[Theory]
113+
[MemberData(nameof(ByteEnums))]
114+
[MemberData(nameof(SByteEnums))]
115+
[MemberData(nameof(Int32Enums))]
116+
[MemberData(nameof(UInt32Enums))]
117+
[MemberData(nameof(Int64Enums))]
118+
public void ConstantEnumOnesComplement(object operand, object result)
119+
{
120+
CallSite<Func<CallSite, object, object>> cs = CallSite<Func<CallSite, object, object>>.Create(
121+
Binder.UnaryOperation(
122+
CSharpBinderFlags.None, ExpressionType.OnesComplement, GetType(),
123+
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) }));
124+
Assert.Equal(result, cs.Target(cs, operand));
125+
}
126+
127+
[Theory]
128+
[MemberData(nameof(ByteEnums))]
129+
[MemberData(nameof(SByteEnums))]
130+
[MemberData(nameof(Int32Enums))]
131+
[MemberData(nameof(UInt32Enums))]
132+
[MemberData(nameof(Int64Enums))]
133+
public void ConstantCheckedEnumOnesComplement(object operand, object result)
134+
{
135+
CallSite<Func<CallSite, object, object>> cs = CallSite<Func<CallSite, object, object>>.Create(
136+
Binder.UnaryOperation(
137+
CSharpBinderFlags.CheckedContext, ExpressionType.OnesComplement, GetType(),
138+
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) }));
139+
Assert.Equal(result, cs.Target(cs, operand));
140+
}
141+
}
142+
}

0 commit comments

Comments
 (0)