Skip to content

Commit 19e69e6

Browse files
Merge pull request #3017 from icsharpcode/operator-checked
2 parents bf0d74d + b80d20b commit 19e69e6

16 files changed

+299
-29
lines changed

ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs

+73
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,28 @@ public class AllOperators
4242
return null;
4343
}
4444

45+
#if CS110
46+
public static AllOperators operator checked +(AllOperators a, AllOperators b)
47+
{
48+
return null;
49+
}
50+
51+
public static AllOperators operator checked -(AllOperators a, AllOperators b)
52+
{
53+
return null;
54+
}
55+
56+
public static AllOperators operator checked *(AllOperators a, AllOperators b)
57+
{
58+
return null;
59+
}
60+
61+
public static AllOperators operator checked /(AllOperators a, AllOperators b)
62+
{
63+
return null;
64+
}
65+
#endif
66+
4567
public static AllOperators operator %(AllOperators a, AllOperators b)
4668
{
4769
return null;
@@ -109,6 +131,23 @@ public class AllOperators
109131
return null;
110132
}
111133

134+
#if CS110
135+
public static AllOperators operator checked -(AllOperators a)
136+
{
137+
return null;
138+
}
139+
140+
public static AllOperators operator checked ++(AllOperators a)
141+
{
142+
return null;
143+
}
144+
145+
public static AllOperators operator checked --(AllOperators a)
146+
{
147+
return null;
148+
}
149+
#endif
150+
112151
public static bool operator true(AllOperators a)
113152
{
114153
return false;
@@ -158,6 +197,13 @@ public static explicit operator int(AllOperators a)
158197
{
159198
return 0;
160199
}
200+
201+
#if CS110
202+
public static explicit operator checked int(AllOperators a)
203+
{
204+
return 0;
205+
}
206+
#endif
161207
}
162208

163209
public class UseAllOperators
@@ -171,6 +217,18 @@ public void Test()
171217
c = a - b;
172218
c = a * b;
173219
c = a / b;
220+
#if CS110
221+
checked
222+
{
223+
c = a + b;
224+
c = a - b;
225+
c = a * b;
226+
c = a / b;
227+
}
228+
// force end of checked block:
229+
++a;
230+
#endif
231+
174232
c = a % b;
175233
c = a & b;
176234
c = a | b;
@@ -186,6 +244,16 @@ public void Test()
186244
c = +a;
187245
c = ++a;
188246
c = --a;
247+
#if CS110
248+
checked
249+
{
250+
c = -a;
251+
c = ++a;
252+
c = --a;
253+
}
254+
// force end of checked block:
255+
++a;
256+
#endif
189257
if (a)
190258
{
191259
Console.WriteLine("a");
@@ -219,6 +287,11 @@ public void Test()
219287
Console.WriteLine("a >= b");
220288
}
221289
int num = (int)a;
290+
#if CS110
291+
num = checked((int)a);
292+
// force end of checked block:
293+
num = (int)a;
294+
#endif
222295
a = num;
223296
}
224297
}

ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

+1
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ static TypeSystemAstBuilder CreateAstBuilder(DecompilerSettings settings)
531531
typeSystemAstBuilder.SupportRecordClasses = settings.RecordClasses;
532532
typeSystemAstBuilder.SupportRecordStructs = settings.RecordStructs;
533533
typeSystemAstBuilder.SupportUnsignedRightShift = settings.UnsignedRightShift;
534+
typeSystemAstBuilder.SupportOperatorChecked = settings.CheckedOperators;
534535
typeSystemAstBuilder.AlwaysUseGlobal = settings.AlwaysUseGlobal;
535536
return typeSystemAstBuilder;
536537
}

ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

+11
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,15 @@ protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(
18101810
{
18111811
target = Translate(inst.Target, loadType);
18121812
}
1813+
var opType = OperatorDeclaration.GetOperatorType(inst.Method.Name);
1814+
if (opType != null && OperatorDeclaration.IsChecked(opType.Value))
1815+
{
1816+
target.Expression.AddAnnotation(AddCheckedBlocks.CheckedAnnotation);
1817+
}
1818+
else if (ReplaceMethodCallsWithOperators.HasCheckedEquivalent(inst.Method))
1819+
{
1820+
target.Expression.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation);
1821+
}
18131822
if (UserDefinedCompoundAssign.IsStringConcat(inst.Method))
18141823
{
18151824
Debug.Assert(inst.Method.Parameters.Count == 2);
@@ -1876,8 +1885,10 @@ protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(
18761885
switch (name)
18771886
{
18781887
case "op_Increment":
1888+
case "op_CheckedIncrement":
18791889
return isPostfix ? UnaryOperatorType.PostIncrement : UnaryOperatorType.Increment;
18801890
case "op_Decrement":
1891+
case "op_CheckedDecrement":
18811892
return isPostfix ? UnaryOperatorType.PostDecrement : UnaryOperatorType.Decrement;
18821893
default:
18831894
return null;

ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs

+17-1
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ TypeSystemAstBuilder CreateAstBuilder()
238238
astBuilder.SupportRecordClasses = (ConversionFlags & ConversionFlags.SupportRecordClasses) != 0;
239239
astBuilder.SupportRecordStructs = (ConversionFlags & ConversionFlags.SupportRecordStructs) != 0;
240240
astBuilder.SupportUnsignedRightShift = (ConversionFlags & ConversionFlags.SupportUnsignedRightShift) != 0;
241+
astBuilder.SupportOperatorChecked = (ConversionFlags & ConversionFlags.SupportOperatorChecked) != 0;
241242
return astBuilder;
242243
}
243244

@@ -296,20 +297,35 @@ void WriteMemberDeclarationName(IMember member, TokenWriter writer, CSharpFormat
296297
ConvertType(member.ReturnType, writer, formattingPolicy);
297298
break;
298299
case "op_Explicit":
300+
case "op_CheckedExplicit":
299301
writer.WriteKeyword(OperatorDeclaration.ExplicitRole, "explicit");
300302
writer.Space();
301303
writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator");
302304
writer.Space();
305+
if (member.Name == "op_CheckedExplicit")
306+
{
307+
writer.WriteToken(OperatorDeclaration.CheckedKeywordRole, "checked");
308+
writer.Space();
309+
}
303310
ConvertType(member.ReturnType, writer, formattingPolicy);
304311
break;
305312
default:
306313
writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator");
307314
writer.Space();
308315
var operatorType = OperatorDeclaration.GetOperatorType(member.Name);
309-
if (operatorType.HasValue)
316+
if (operatorType.HasValue && !((ConversionFlags & ConversionFlags.SupportOperatorChecked) == 0 && OperatorDeclaration.IsChecked(operatorType.Value)))
317+
{
318+
if (OperatorDeclaration.IsChecked(operatorType.Value))
319+
{
320+
writer.WriteToken(OperatorDeclaration.CheckedKeywordRole, "checked");
321+
writer.Space();
322+
}
310323
writer.WriteToken(OperatorDeclaration.GetRole(operatorType.Value), OperatorDeclaration.GetToken(operatorType.Value));
324+
}
311325
else
326+
{
312327
writer.WriteIdentifier(node.NameToken);
328+
}
313329
break;
314330
}
315331
break;

ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

+14-3
Original file line numberDiff line numberDiff line change
@@ -2528,7 +2528,7 @@ public virtual void VisitOperatorDeclaration(OperatorDeclaration operatorDeclara
25282528
StartNode(operatorDeclaration);
25292529
WriteAttributes(operatorDeclaration.Attributes);
25302530
WriteModifiers(operatorDeclaration.ModifierTokens);
2531-
if (operatorDeclaration.OperatorType == OperatorType.Explicit)
2531+
if (operatorDeclaration.OperatorType == OperatorType.Explicit || operatorDeclaration.OperatorType == OperatorType.CheckedExplicit)
25322532
{
25332533
WriteKeyword(OperatorDeclaration.ExplicitRole);
25342534
}
@@ -2542,7 +2542,13 @@ public virtual void VisitOperatorDeclaration(OperatorDeclaration operatorDeclara
25422542
}
25432543
WriteKeyword(OperatorDeclaration.OperatorKeywordRole);
25442544
Space();
2545+
if (OperatorDeclaration.IsChecked(operatorDeclaration.OperatorType))
2546+
{
2547+
WriteKeyword(OperatorDeclaration.CheckedKeywordRole);
2548+
Space();
2549+
}
25452550
if (operatorDeclaration.OperatorType == OperatorType.Explicit
2551+
|| operatorDeclaration.OperatorType == OperatorType.CheckedExplicit
25462552
|| operatorDeclaration.OperatorType == OperatorType.Implicit)
25472553
{
25482554
operatorDeclaration.ReturnType.AcceptVisitor(this);
@@ -3082,7 +3088,7 @@ public virtual void VisitDocumentationReference(DocumentationReference documenta
30823088
break;
30833089
case SymbolKind.Operator:
30843090
var opType = documentationReference.OperatorType;
3085-
if (opType == OperatorType.Explicit)
3091+
if (opType == OperatorType.Explicit || opType == OperatorType.CheckedExplicit)
30863092
{
30873093
WriteKeyword(OperatorDeclaration.ExplicitRole);
30883094
}
@@ -3092,7 +3098,12 @@ public virtual void VisitDocumentationReference(DocumentationReference documenta
30923098
}
30933099
WriteKeyword(OperatorDeclaration.OperatorKeywordRole);
30943100
Space();
3095-
if (opType == OperatorType.Explicit || opType == OperatorType.Implicit)
3101+
if (OperatorDeclaration.IsChecked(opType))
3102+
{
3103+
WriteKeyword(OperatorDeclaration.CheckedKeywordRole);
3104+
Space();
3105+
}
3106+
if (opType == OperatorType.Explicit || opType == OperatorType.Implicit || opType == OperatorType.CheckedExplicit)
30963107
{
30973108
documentationReference.ConversionOperatorReturnType.AcceptVisitor(this);
30983109
}

ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs

+51-9
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,25 @@ public enum OperatorType
3737
LogicalNot,
3838
OnesComplement,
3939
Increment,
40+
CheckedIncrement,
4041
Decrement,
42+
CheckedDecrement,
4143
True,
4244
False,
4345

44-
// Unary and Binary operators
45-
Addition,
46-
Subtraction,
47-
4846
UnaryPlus,
4947
UnaryNegation,
48+
CheckedUnaryNegation,
5049

5150
// Binary operators
51+
Addition,
52+
CheckedAddition,
53+
Subtraction,
54+
CheckedSubtraction,
5255
Multiply,
56+
CheckedMultiply,
5357
Division,
58+
CheckedDivision,
5459
Modulus,
5560
BitwiseAnd,
5661
BitwiseOr,
@@ -67,12 +72,14 @@ public enum OperatorType
6772

6873
// Implicit and Explicit
6974
Implicit,
70-
Explicit
75+
Explicit,
76+
CheckedExplicit
7177
}
7278

7379
public class OperatorDeclaration : EntityDeclaration
7480
{
7581
public static readonly TokenRole OperatorKeywordRole = new TokenRole("operator");
82+
public static readonly TokenRole CheckedKeywordRole = new TokenRole("checked");
7683

7784
// Unary operators
7885
public static readonly TokenRole LogicalNotRole = new TokenRole("!");
@@ -110,19 +117,26 @@ public class OperatorDeclaration : EntityDeclaration
110117

111118
static OperatorDeclaration()
112119
{
113-
names = new string[(int)OperatorType.Explicit + 1][];
120+
names = new string[(int)OperatorType.CheckedExplicit + 1][];
114121
names[(int)OperatorType.LogicalNot] = new string[] { "!", "op_LogicalNot" };
115122
names[(int)OperatorType.OnesComplement] = new string[] { "~", "op_OnesComplement" };
116123
names[(int)OperatorType.Increment] = new string[] { "++", "op_Increment" };
124+
names[(int)OperatorType.CheckedIncrement] = new string[] { "++", "op_CheckedIncrement" };
117125
names[(int)OperatorType.Decrement] = new string[] { "--", "op_Decrement" };
126+
names[(int)OperatorType.CheckedDecrement] = new string[] { "--", "op_CheckedDecrement" };
118127
names[(int)OperatorType.True] = new string[] { "true", "op_True" };
119128
names[(int)OperatorType.False] = new string[] { "false", "op_False" };
120-
names[(int)OperatorType.Addition] = new string[] { "+", "op_Addition" };
121-
names[(int)OperatorType.Subtraction] = new string[] { "-", "op_Subtraction" };
122129
names[(int)OperatorType.UnaryPlus] = new string[] { "+", "op_UnaryPlus" };
123130
names[(int)OperatorType.UnaryNegation] = new string[] { "-", "op_UnaryNegation" };
131+
names[(int)OperatorType.CheckedUnaryNegation] = new string[] { "-", "op_CheckedUnaryNegation" };
132+
names[(int)OperatorType.Addition] = new string[] { "+", "op_Addition" };
133+
names[(int)OperatorType.CheckedAddition] = new string[] { "+", "op_CheckedAddition" };
134+
names[(int)OperatorType.Subtraction] = new string[] { "-", "op_Subtraction" };
135+
names[(int)OperatorType.CheckedSubtraction] = new string[] { "-", "op_CheckedSubtraction" };
124136
names[(int)OperatorType.Multiply] = new string[] { "*", "op_Multiply" };
137+
names[(int)OperatorType.CheckedMultiply] = new string[] { "*", "op_CheckedMultiply" };
125138
names[(int)OperatorType.Division] = new string[] { "/", "op_Division" };
139+
names[(int)OperatorType.CheckedDivision] = new string[] { "/", "op_CheckedDivision" };
126140
names[(int)OperatorType.Modulus] = new string[] { "%", "op_Modulus" };
127141
names[(int)OperatorType.BitwiseAnd] = new string[] { "&", "op_BitwiseAnd" };
128142
names[(int)OperatorType.BitwiseOr] = new string[] { "|", "op_BitwiseOr" };
@@ -138,6 +152,7 @@ static OperatorDeclaration()
138152
names[(int)OperatorType.LessThanOrEqual] = new string[] { "<=", "op_LessThanOrEqual" };
139153
names[(int)OperatorType.Implicit] = new string[] { "implicit", "op_Implicit" };
140154
names[(int)OperatorType.Explicit] = new string[] { "explicit", "op_Explicit" };
155+
names[(int)OperatorType.CheckedExplicit] = new string[] { "explicit", "op_CheckedExplicit" };
141156
}
142157

143158
public override SymbolKind SymbolKind {
@@ -202,24 +217,31 @@ public static TokenRole GetRole(OperatorType type)
202217
case OperatorType.OnesComplement:
203218
return OnesComplementRole;
204219
case OperatorType.Increment:
220+
case OperatorType.CheckedIncrement:
205221
return IncrementRole;
206222
case OperatorType.Decrement:
223+
case OperatorType.CheckedDecrement:
207224
return DecrementRole;
208225
case OperatorType.True:
209226
return TrueRole;
210227
case OperatorType.False:
211228
return FalseRole;
212229

213230
case OperatorType.Addition:
231+
case OperatorType.CheckedAddition:
214232
case OperatorType.UnaryPlus:
215233
return AdditionRole;
216234
case OperatorType.Subtraction:
235+
case OperatorType.CheckedSubtraction:
217236
case OperatorType.UnaryNegation:
237+
case OperatorType.CheckedUnaryNegation:
218238
return SubtractionRole;
219239

220240
case OperatorType.Multiply:
241+
case OperatorType.CheckedMultiply:
221242
return MultiplyRole;
222243
case OperatorType.Division:
244+
case OperatorType.CheckedDivision:
223245
return DivisionRole;
224246
case OperatorType.Modulus:
225247
return ModulusRole;
@@ -251,6 +273,7 @@ public static TokenRole GetRole(OperatorType type)
251273
case OperatorType.Implicit:
252274
return ImplicitRole;
253275
case OperatorType.Explicit:
276+
case OperatorType.CheckedExplicit:
254277
return ExplicitRole;
255278

256279
default:
@@ -269,7 +292,26 @@ public static string GetName(OperatorType? type)
269292
}
270293

271294
/// <summary>
272-
/// Gets the token for the operator type ("+", "implicit", etc.)
295+
/// Gets whether the operator type is a C# 11 "operator checked".
296+
/// </summary>
297+
public static bool IsChecked(OperatorType type)
298+
{
299+
return type switch {
300+
OperatorType.CheckedAddition => true,
301+
OperatorType.CheckedSubtraction => true,
302+
OperatorType.CheckedMultiply => true,
303+
OperatorType.CheckedDivision => true,
304+
OperatorType.CheckedUnaryNegation => true,
305+
OperatorType.CheckedIncrement => true,
306+
OperatorType.CheckedDecrement => true,
307+
OperatorType.CheckedExplicit => true,
308+
_ => false,
309+
};
310+
}
311+
312+
/// <summary>
313+
/// Gets the token for the operator type ("+", "implicit", etc.).
314+
/// Does not include the "checked" modifier.
273315
/// </summary>
274316
public static string GetToken(OperatorType type)
275317
{

0 commit comments

Comments
 (0)