diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index e1b70661a2c16a1d9d79cd139d02f696ec18621b..0ad660f0d8ea22b49c68fe36b7d5d345144f30ba 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -6,26 +6,27 @@ This document reflects the status, and planned work, for the compiler team. It | Feature | Branch | State | Owners | LDM Champ | | ------- | ------ | ----- | ------ | --------- | -| Address of Static | none | Feature Specification | | [jaredpar](https://github.com/jaredpar) | | [Binary Literals](https://github.com/dotnet/roslyn/issues/215) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | | [gafter](https://github.com/gafter) | | [Digit Separators](https://github.com/dotnet/roslyn/issues/216) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | | [gafter](https://github.com/gafter) | -| [Local Functions](https://github.com/dotnet/roslyn/blob/master/docs/features/local-functions.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [agocke](https://github.com/agocke), [jaredpar](https://github.com/jaredpar), [vsadov](https://github.com/vsadov) | [gafter](https://github.com/gafter) | -| [Type switch](https://github.com/dotnet/roslyn/blob/master/docs/features/patterns.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [gafter](https://github.com/gafter), [alekseyts](https://github.com/alekseyts), [agocke](https://github.com/agocke) | [gafter](https://github.com/gafter) | +| [Local Functions](features/local-functions.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [agocke](https://github.com/agocke), [jaredpar](https://github.com/jaredpar), [vsadov](https://github.com/vsadov) | [gafter](https://github.com/gafter) | +| [Type switch](features/patterns.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [gafter](https://github.com/gafter), [alekseyts](https://github.com/alekseyts), [agocke](https://github.com/agocke) | [gafter](https://github.com/gafter) | | [Ref Returns](https://github.com/dotnet/roslyn/issues/118) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [vsadov](https://github.com/vsadov), [agocke](https://github.com/agocke), [jaredpar](https://github.com/jaredpar) | [vsadov](https://github.com/vsadov) | | [Tuples](https://github.com/dotnet/roslyn/issues/347) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [vsadov](https://github.com/vsadov), [jcouv](https://github.com/jcouv) | [madstorgersen](https://github.com/MadsTorgersen) | -| [Out var](https://github.com/dotnet/roslyn/blob/features/outvar/docs/features/outvar.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [alekseyts](https://github.com/alekseyts) | [gafter](https://github.com/gafter) | +| [Out var](features/outvar.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [alekseyts](https://github.com/alekseyts) | [gafter](https://github.com/gafter) | | [ValueTask](https://github.com/ljw1004/roslyn/blob/features/async-return/docs/specs/feature%20-%20arbitrary%20async%20returns.md) | [master](https://github.com/dotnet/roslyn/tree/master) | Finishing | [alekseyts](https://github.com/alekseyts) | [lucian](https://github.com/ljw1004) | +| [Throw Expr](features/throwexpr.md) | [features/throwexpr](https://github.com/dotnet/roslyn/tree/features/throwexpr) | Prototyping | [gafter](https://github.com/gafter), [agocke](https://github.com/agocke), [tyoverby](https://github.com/tyoverby) | [gafter](https://github.com/gafter) | +| [Expression-Bodied Everything](https://github.com/dotnet/roslyn/issues/7881) | [features/exprbody](https://github.com/dotnet/roslyn/tree/features/exprbody) | Prototyping | [MgSam](https://github.com/MgSam), [gafter](https://github.com/gafter) | [madstorgersen](https://github.com/MadsTorgersen) | ## (C# 7.0 and VB 15) + 1 | Feature | Branch | State | Owners | LDM Champ | | ------- | ------ | ----- | ------ | --------- | +| Address of Static | none | Feature Specification | | [jaredpar](https://github.com/jaredpar) | | [Async Main](https://github.com/dotnet/roslyn/issues/7476) | none | Feature Specification | [tyoverby](https://github.com/tyoverby), [agocke](https://github.com/agocke) | [stephentoub](https://github.com/stephentoub) | | [Source Generation](https://github.com/dotnet/roslyn/blob/master/docs/features/generators.md) | [master](https://github.com/dotnet/roslyn/tree/features/generators) | Prototyping | [cston](https://github.com/cston), [vsadov](https://github.com/vsadov), [agocke](https://github.com/agocke) | [mattwar](https://github.com/mattwar) | -| [Throw Expr](https://github.com/dotnet/roslyn/blob/features/patterns/docs/features/patterns.md) | [features/patterns](https://github.com/dotnet/roslyn/tree/features/patterns) | Prototyping | [agocke](https://github.com/agocke), [tyoverby](https://github.com/tyoverby), [gafter](https://github.com/gafter) | [gafter](https://github.com/gafter) | | [private protected](https://github.com/dotnet/roslyn/blob/features/privateProtected/docs/features/private-protected.md) | [features/privateProtected](https://github.com/dotnet/roslyn/tree/features/privateProtected) | Prototyping | | [gafter](https://github.com/gafter) | | [Non-null Ref Types](https://github.com/dotnet/roslyn/blob/features/NullableReferenceTypes/docs/features/NullableReferenceTypes/Nullable%20reference%20types.md) | [features/NullableReferenceTypes](https://github.com/dotnet/roslyn/tree/features/NullableReferenceTypes) | Prototyping | [alekseyts](https://github.com/alekseyts) | [mattwar](https://github.com/mattwar) | -| [Better Betterness](https://github.com/dotnet/roslyn/issues/250) | none | Feature Specification | | [gafter](https://github.com/gafter) | +| [Bestest Betterness](https://github.com/dotnet/roslyn/issues/250) | none | Feature Specification | | [gafter](https://github.com/gafter) | | [Records](https://github.com/dotnet/roslyn/blob/features/records/docs/features/records.md) | [features/records](https://github.com/dotnet/roslyn/tree/features/records) | Feature Specification | [jcouv](https://github.com/jcouv) | [gafter](https://github.com/gafter) | | [With Exprs](https://github.com/dotnet/roslyn/blob/features/records/docs/features/records.md) | [features/records](https://github.com/dotnet/roslyn/tree/features/records) | Feature Specification | [gafter](https://github.com/gafter) | [gafter](https://github.com/gafter) | | [Pattern Matching](https://github.com/dotnet/roslyn/blob/features/patterns/docs/features/patterns.md) | [features/patterns](https://github.com/dotnet/roslyn/tree/features/patterns) | Prototyping | [gafter](https://github.com/gafter), [alekseyts](https://github.com/alekseyts), [agocke](https://github.com/agocke) | [gafter](https://github.com/gafter) | @@ -35,4 +36,4 @@ This document reflects the status, and planned work, for the compiler team. It - **Is target version a guarantee?**: No. It's explicitly not a guarantee. This is just the planned and ongoing work to the best of our knowledge at this time. - **Where are these State values defined?**: Take a look at the [Developing a Language Feature](contributing/Developing a Language Feature.md) document. -Updated 2016-07-27 \ No newline at end of file +Updated 2016-08-29 \ No newline at end of file diff --git a/docs/features/throwexpr.md b/docs/features/throwexpr.md new file mode 100644 index 0000000000000000000000000000000000000000..bf6647dc9edee26e2f657c8218992ed9a34a9d66 --- /dev/null +++ b/docs/features/throwexpr.md @@ -0,0 +1,30 @@ +## Throw expression + +We extend the set of expression forms to include + +```antlr +throw_expression + : 'throw' null_coalescing_expression + ; + +null_coalescing_expression + : throw_expression + ; +``` + +The type rules are as follows: + +- A *throw_expression* has no type. +- A *throw_expression* is convertible to every type by an implicit conversion. + +The flow-analysis rules are as follows: + +- For every variable *v*, *v* is definitely assigned before the *null_coalescing_expression* of a *throw_expression* iff it is definitely assigned before the *throw_expression*. +- For every variable *v*, *v* is definitely assigned after *throw_expression*. + +A *throw expression* is permitted in only the following syntactic contexts: +- As the second or third operand of a ternary conditional operator `?:` +- As the second operand of a null coalescing operator `??` +- As the body of an expression-bodied lambda or method. + +> Note: the rest of the semantics of the throw expression are identical to the semantics of the *throw_statement* in the current language specification. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 5c7f29ac5fb6480dc96833478340f5d9c8db15d5..06ea5f338d14db4bfe9227203ac04a80a712d81e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -628,6 +628,9 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic case SyntaxKind.TupleExpression: return BindTupleExpression((TupleExpressionSyntax)node, diagnostics); + case SyntaxKind.ThrowExpression: + return BindThrowExpression((ThrowExpressionSyntax)node, diagnostics); + case SyntaxKind.RefType: { var firstToken = node.GetFirstToken(); @@ -653,6 +656,53 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic } } + private BoundExpression BindThrowExpression(ThrowExpressionSyntax node, DiagnosticBag diagnostics) + { + bool hasErrors = node.HasErrors; + if (!IsThrowExpressionInProperContext(node)) + { + diagnostics.Add(ErrorCode.ERR_ThrowMisplaced, node.ThrowKeyword.GetLocation()); + hasErrors = true; + } + + var thrownExpression = BindThrownExpression(node.Expression, diagnostics, ref hasErrors); + return new BoundThrowExpression(node, thrownExpression, null, hasErrors); + } + + private static bool IsThrowExpressionInProperContext(ThrowExpressionSyntax node) + { + var parent = node.Parent; + if (parent == null || node.HasErrors) + { + return true; + } + + switch (node.Parent.Kind()) + { + case SyntaxKind.ConditionalExpression: // ?: + { + var conditionalParent = (ConditionalExpressionSyntax)parent; + return node == conditionalParent.WhenTrue || node == conditionalParent.WhenFalse; + } + case SyntaxKind.CoalesceExpression: // ?? + { + var binaryParent = (BinaryExpressionSyntax)parent; + return node == binaryParent.Right; + } + case SyntaxKind.ArrowExpressionClause: // => + { + var arrowClauseParent = (ArrowExpressionClauseSyntax)parent; + return node == arrowClauseParent.Expression; + } + // We do not support && and || because + // 1. The precedence would not syntactically allow it + // 2. It isn't clear what the semantics should be + // 3. It isn't clear what use cases would motivate us to change the precedence to support it + default: + return false; + } + } + private BoundExpression BindTupleExpression(TupleExpressionSyntax node, DiagnosticBag diagnostics) { SeparatedSyntaxList arguments = node.Arguments; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 34b768b82ac125225640e4ea16bcb0f41425af11..3cc7a5ef789cbbf486d12b25cfafc374c67fc2d8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3575,10 +3575,10 @@ internal BoundBlock CreateBlockFromExpression(CSharpSyntaxNode node, ImmutableAr // If the return type is void then the expression is required to be a legal // statement expression. - Debug.Assert(expressionSyntax != null || !IsValidStatementExpression(expression.Syntax, expression)); + Debug.Assert(expressionSyntax != null || !IsValidExpressionBody(expressionSyntax, expression)); bool errors = false; - if (expressionSyntax == null || !IsValidStatementExpression(expressionSyntax, expression)) + if (expressionSyntax == null || !IsValidExpressionBody(expressionSyntax, expression)) { Error(diagnostics, ErrorCode.ERR_IllegalStatement, syntax); errors = true; @@ -3609,6 +3609,11 @@ internal BoundBlock CreateBlockFromExpression(CSharpSyntaxNode node, ImmutableAr return new BoundBlock(node, locals, ImmutableArray.Create(statement)) { WasCompilerGenerated = node.Kind() != SyntaxKind.ArrowExpressionClause }; } + private static bool IsValidExpressionBody(SyntaxNode expressionSyntax, BoundExpression expression) + { + return IsValidStatementExpression(expressionSyntax, expression) || expressionSyntax.Kind() == SyntaxKind.ThrowExpression; + } + /// /// Binds an expression-bodied member with expression e as either { return e;} or { e; }. /// diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index f02f86392dd47d6e3ae7a91069a6f1c1d567cf75..1f2d3f19f3c1cead39be375b6a87766b1f9a4be3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -156,6 +156,7 @@ private static void AssertTrivialConversion(ConversionKind kind) case ConversionKind.ImplicitNumeric: case ConversionKind.ImplicitReference: case ConversionKind.ImplicitEnumeration: + case ConversionKind.ImplicitThrow: case ConversionKind.AnonymousFunction: case ConversionKind.Boxing: case ConversionKind.NullLiteral: @@ -195,6 +196,7 @@ internal static Conversion GetTrivialConversion(ConversionKind kind) internal static Conversion ImplicitNumeric => new Conversion(ConversionKind.ImplicitNumeric); internal static Conversion ImplicitReference => new Conversion(ConversionKind.ImplicitReference); internal static Conversion ImplicitEnumeration => new Conversion(ConversionKind.ImplicitEnumeration); + internal static Conversion ImplicitThrow => new Conversion(ConversionKind.ImplicitThrow); internal static Conversion AnonymousFunction => new Conversion(ConversionKind.AnonymousFunction); internal static Conversion Boxing => new Conversion(ConversionKind.Boxing); internal static Conversion NullLiteral => new Conversion(ConversionKind.NullLiteral); @@ -441,6 +443,17 @@ public bool IsEnumeration } } + /// + /// Returns true if the conversion is an implicit throw conversion. + /// + public bool IsThrow + { + get + { + return Kind == ConversionKind.ImplicitThrow; + } + } + // TODO: update the language reference section number below. /// /// Returns true if the conversion is an interpolated string conversion. diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs index cef2188dc3630a107ad8ee2d41ad53993a232886..c678015b75e8303e856017cec09a737b87eb617e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs @@ -12,6 +12,7 @@ internal enum ConversionKind : byte Identity, ImplicitNumeric, ImplicitEnumeration, + ImplicitThrow, ImplicitTupleLiteral, ImplicitTuple, ExplicitTupleLiteral, diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs index a136e5575ce3860d5a5d0b9c89f9e6b9777f4615..6da4c0e02545730d93c9f22e3ff235df84d82d19 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs @@ -28,6 +28,7 @@ public static bool IsImplicitConversion(this ConversionKind conversionKind) case ConversionKind.ImplicitTupleLiteral: case ConversionKind.ImplicitTuple: case ConversionKind.ImplicitEnumeration: + case ConversionKind.ImplicitThrow: case ConversionKind.ImplicitNullable: case ConversionKind.NullLiteral: case ConversionKind.ImplicitReference: diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 1854a72eb9be0794301b32d17338b9ae610d4705..f47e99595cf89f67b545bbb8d3b31964f6f80ed0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -837,6 +837,9 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi return interpolatedStringConversion; } break; + + case BoundKind.ThrowExpression: + return Conversion.ImplicitThrow; } return Conversion.NoConversion; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs index 3d4fde5f8fbf8df79f770c5d531f3a5ee630afbe..36cd8cf1c7ba5826a07cc08f7d1567ead5ba8a1c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs @@ -600,6 +600,7 @@ private static bool IsEncompassingImplicitConversionKind(ConversionKind kind) // Added for C# 7. case ConversionKind.ImplicitTupleLiteral: case ConversionKind.ImplicitTuple: + case ConversionKind.ImplicitThrow: return true; case ConversionKind.ExplicitTupleLiteral: diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 7afcf851d5b1a5c0883df0ab515ed8c64249a2a2..d2fe8259b7386d3f423f8de8372bf416cfda0bb0 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1581,6 +1581,10 @@ + + + + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 0c9d393c3a9c9fe485edc0b06f95f9c50df1ef39..23c6d54566c5ce348542ad9d875f0fa63533dae7 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -725,6 +725,7 @@ Semantics.ConversionKind IConversionExpression.ConversionKind case CSharp.ConversionKind.ImplicitDynamic: case CSharp.ConversionKind.ExplicitEnumeration: case CSharp.ConversionKind.ImplicitEnumeration: + case CSharp.ConversionKind.ImplicitThrow: case CSharp.ConversionKind.ImplicitTupleLiteral: case CSharp.ConversionKind.ImplicitTuple: case CSharp.ConversionKind.ExplicitTupleLiteral: @@ -3017,6 +3018,24 @@ protected override OperationKind ExpressionKind } } + partial class BoundThrowExpression + { + public override void Accept(OperationVisitor visitor) + { + // TODO: implement IOperation for pattern-matching constructs (https://github.com/dotnet/roslyn/issues/8699) + visitor.VisitNoneOperation(this); + } + + public override TResult Accept(OperationVisitor visitor, TArgument argument) + { + // TODO: implement IOperation for pattern-matching constructs (https://github.com/dotnet/roslyn/issues/8699) + return visitor.VisitNoneOperation(this, argument); + } + + // TODO: implement IOperation for pattern-matching constructs (https://github.com/dotnet/roslyn/issues/8699) + protected override OperationKind ExpressionKind => OperationKind.None; + } + internal partial class BoundDeclarationPattern { public BoundDeclarationPattern(SyntaxNode syntax, LocalSymbol localSymbol, BoundTypeExpression declaredType, bool isVar, bool hasErrors = false) diff --git a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs index f8bd809d6070302adc345f5c46a5193ea978cfdf..4610eec32e0e4c4a74bd69018e911df0c7a2ebf2 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs @@ -61,6 +61,14 @@ public override object Display } } + internal sealed partial class BoundThrowExpression + { + public override object Display + { + get { return MessageID.IDS_ThrowExpression.Localize(); } + } + } + internal partial class BoundTupleExpression { public override object Display diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 2011e510ccaaec579c8317824f6135ec802058b9..aa5254af36c3e7e1844ba4e2bcf12d6a74552b0f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -8755,6 +8755,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to A throw expression is not allowed in this context.. + /// + internal static string ERR_ThrowMisplaced { + get { + return ResourceManager.GetString("ERR_ThrowMisplaced", resourceCulture); + } + } + /// /// Looks up a localized string similar to Catch clauses cannot follow the general catch clause of a try statement. /// @@ -9998,6 +10007,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to throw expression. + /// + internal static string IDS_FeatureThrowExpression { + get { + return ResourceManager.GetString("IDS_FeatureThrowExpression", resourceCulture); + } + } + /// /// Looks up a localized string similar to tuples. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 8d4c462f9f1edaf33d804630ecd3ae341b6cb973..296d0b4652cfd10670b47b48c5711f5ff3c4c39e 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -120,6 +120,9 @@ <null> + + <throw expression> + (Location of symbol related to previous error) @@ -210,6 +213,9 @@ pattern matching + + throw expression + implicitly typed array @@ -4953,4 +4959,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers. + + A throw expression is not allowed in this context. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs index eecc415aff1db312eaf2dfb34b9a427891e66240..4b0fbf604121554740b5fb5c8f06e3d6f3f0a690 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs @@ -75,6 +75,7 @@ private void EmitConversion(BoundConversion conversion) case ConversionKind.ExplicitTuple: case ConversionKind.ImplicitDynamic: case ConversionKind.ExplicitDynamic: + case ConversionKind.ImplicitThrow: // None of these things should reach codegen (yet? maybe?) throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind); case ConversionKind.PointerToVoid: diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 3dc7d31c60224f5fd87b1cdc1e2e17a370a49042..e03639e301a6332c363be799d1b3498312dc9342 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -302,6 +302,10 @@ private void EmitExpressionCore(BoundExpression expression, bool used) EmitPseudoVariableValue((BoundPseudoVariable)expression, used); break; + case BoundKind.ThrowExpression: + EmitThrowExpression((BoundThrowExpression)expression, used); + break; + case BoundKind.Void: Debug.Assert(!used); break; @@ -315,6 +319,14 @@ private void EmitExpressionCore(BoundExpression expression, bool used) } } + private void EmitThrowExpression(BoundThrowExpression node, bool used) + { + this.EmitThrow(node.Expression); + + // to satisfy invariants, we push a default value to pretend to adjust the stack height + EmitDefaultValue(node.Type, used, node.Syntax); + } + private void EmitComplexConditionalReceiver(BoundComplexConditionalReceiver expression, bool used) { Debug.Assert(!expression.Type.IsReferenceType); diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs index b7a2944965d0f4e6b726b3ffe7c836bab3abe127..d7e43cdd2d7f411ff030c646c4c59f6067100c68 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs @@ -140,20 +140,24 @@ private void EmitNoOpStatement(BoundNoOpStatement statement) private void EmitThrowStatement(BoundThrowStatement node) { - BoundExpression expr = node.ExpressionOpt; - if (expr != null) + EmitThrow(node.ExpressionOpt); + } + + private void EmitThrow(BoundExpression thrown) + { + if (thrown != null) { - this.EmitExpression(expr, true); + this.EmitExpression(thrown, true); - var exprType = expr.Type; + var exprType = thrown.Type; // Expression type will be null for "throw null;". if (exprType?.TypeKind == TypeKind.TypeParameter) { - this.EmitBox(exprType, expr.Syntax); + this.EmitBox(exprType, thrown.Syntax); } } - _builder.EmitThrow(isRethrow: expr == null); + _builder.EmitThrow(isRethrow: thrown == null); } private void EmitConditionalGoto(BoundConditionalGoto boundConditionalGoto) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 4f71a8ed4816f55354be7b3b9b1d0a9d853c64ce..8575980456288c11d54a426c8511f7c1451d9e3e 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1354,6 +1354,7 @@ internal enum ErrorCode // Available = 8112, 8113, 8114, 8115 #region diagnostics for pattern-matching introduced in C# 7 + ERR_ThrowMisplaced = 8115, ERR_PatternNullableType = 8116, ERR_BadIsPatternExpression = 8117, ERR_SwitchExpressionValueExpected = 8119, diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 5a6c5dd56cd4362c475febe226d1b01031f87ec8..bd022c7bfe2efe45025afc7ba287514e1152aff3 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -56,6 +56,7 @@ internal enum MessageID IDS_FeatureNullable = MessageBase + 12528, IDS_Lambda = MessageBase + 12531, IDS_FeaturePatternMatching = MessageBase + 12532, + IDS_FeatureThrowExpression = MessageBase + 12533, IDS_FeatureImplicitArray = MessageBase + 12557, IDS_FeatureImplicitLocal = MessageBase + 12558, @@ -122,6 +123,7 @@ internal enum MessageID IDS_FeatureOutVar = MessageBase + 12713, IDS_FeatureIOperation = MessageBase + 12714, + IDS_ThrowExpression = MessageBase + 12715, } // Message IDs may refer to strings that need to be localized. @@ -184,6 +186,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureLocalFunctions: case MessageID.IDS_FeatureRefLocalsReturns: case MessageID.IDS_FeaturePatternMatching: + case MessageID.IDS_FeatureThrowExpression: case MessageID.IDS_FeatureTuples: case MessageID.IDS_FeatureOutVar: return LanguageVersion.CSharp7; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs index c784d9556784e91239573ae6baf36657cb7aab51..b5b68351c96f4938c467c05c625d69ce8d238fa9 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs @@ -923,6 +923,13 @@ public override BoundNode VisitAttribute(BoundAttribute node) return null; } + public override BoundNode VisitThrowExpression(BoundThrowExpression node) + { + VisitRvalue(node.Expression); + SetUnreachable(); + return node; + } + public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node) { VisitRvalue(node.Expression); diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs index 2bbaf304d6cb72c9b12959684cff35bb3c6665ed..3c9bf481de408f8fc06ec8393eee5f2bc9117bf3 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs @@ -1109,6 +1109,13 @@ public override BoundNode VisitSequence(BoundSequence node) return builder.Update(value); } + public override BoundNode VisitThrowExpression(BoundThrowExpression node) + { + BoundSpillSequenceBuilder builder = null; + BoundExpression operand = VisitExpression(ref builder, node.Expression); + return UpdateExpression(builder, node.Update(operand, node.Type)); + } + /// /// If an expression node that declares synthesized short-lived locals (currently only sequence) contains an await, these locals become long-lived since their /// values may be read by code that follows the await. We promote these variables to long-lived of kind . diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 65d833375b4b71523c1cb6bf6736f921f82389ce..9278e4cee1ee16457dbbeeb78cb04dde41478218 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -328,6 +328,7 @@ private static bool IsSafeForReordering(BoundExpression expression, RefKind kind // expression trees rewrite this later. // it is a kind of user defined conversions on IntPtr and in some cases can fail case ConversionKind.IntPtr: + case ConversionKind.ImplicitThrow: return false; default: diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index fd3341f590478b653d3039856fbd243f4f8b2bb4..96d6c6a0f4fc0e97c098f44a86ffa5951414ef36 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -235,6 +235,13 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew return rewrittenOperand; } + case ConversionKind.ImplicitThrow: + { + // the operand must be a bound throw expression + var operand = (BoundThrowExpression)rewrittenOperand; + return _factory.ThrowExpression(operand.Expression, rewrittenType); + } + case ConversionKind.ImplicitEnumeration: // A conversion from constant zero to nullable is actually classified as an // implicit enumeration conversion, not an implicit nullable conversion. diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index deb1c20e8bd8832a2c1778125e76cffc72592889..7c435662ea714733b9228cbfce3144dd6cd48a6d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -941,6 +941,11 @@ public BoundStatement ThrowNull() return Throw(Null(Compilation.GetWellKnownType(Microsoft.CodeAnalysis.WellKnownType.System_Exception))); } + public BoundExpression ThrowExpression(BoundExpression thrown, TypeSymbol type) + { + return new BoundThrowExpression(thrown.Syntax, thrown, type) { WasCompilerGenerated = true }; + } + public BoundExpression Null(TypeSymbol type) { BoundExpression nullLiteral = new BoundLiteral(Syntax, ConstantValue.Null, type) { WasCompilerGenerated = true }; diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 5e059cca2db4410bb7d57d0f55935c95cdd609ca..05fd500cf33ed84d9269f7b4e34c05bafc730052 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -8986,6 +8986,7 @@ private bool IsPossibleExpression() case SyntaxKind.NewKeyword: case SyntaxKind.DelegateKeyword: case SyntaxKind.ColonColonToken: // bad aliased name + case SyntaxKind.ThrowKeyword: return true; case SyntaxKind.IdentifierToken: // Specifically allow the from contextual keyword, because it can always be the start of an @@ -9279,6 +9280,13 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) skipped = this.AddError(skipped, ErrorCode.ERR_InvalidExprTerm, this.CurrentToken.Text); leftOperand = AddTrailingSkippedSyntax(this.CreateMissingIdentifierName(), skipped); } + else if (tk == SyntaxKind.ThrowKeyword) + { + var result = ParseThrowExpression(); + // we parse a throw expression even at the wrong precedence for better recovery + return (precedence <= Precedence.Coalescing) ? result : + this.AddError(result, ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(tk)); + } else { // Not a unary operator - get a primary expression. @@ -9404,6 +9412,14 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) return leftOperand; } + private ExpressionSyntax ParseThrowExpression() + { + var throwToken = this.EatToken(SyntaxKind.ThrowKeyword); + var thrown = this.ParseSubExpression(Precedence.Coalescing); + var result = _syntaxFactory.ThrowExpression(throwToken, thrown); + return CheckFeatureAvailability(result, MessageID.IDS_FeatureThrowExpression); + } + private ExpressionSyntax ParseIsExpression(ExpressionSyntax leftOperand, SyntaxToken opToken) { var node = this.ParseTypeOrPattern(); diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 948e83df54b946e9b747b7dd36c35e7157929ce9..cef6a65e953130fd3fe49150126345a7aadafd6d 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -5,6 +5,7 @@ *REMOVED*override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode.SerializeTo(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> void Microsoft.CodeAnalysis.CSharp.CSharpParseOptions.CSharpParseOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion languageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.Default, Microsoft.CodeAnalysis.DocumentationMode documentationMode = Microsoft.CodeAnalysis.DocumentationMode.Parse, Microsoft.CodeAnalysis.SourceCodeKind kind = Microsoft.CodeAnalysis.SourceCodeKind.Regular, System.Collections.Generic.IEnumerable preprocessorSymbols = null) -> void Microsoft.CodeAnalysis.CSharp.CSharpParseOptions.SpecifiedLanguageVersion.get -> Microsoft.CodeAnalysis.CSharp.LanguageVersion +Microsoft.CodeAnalysis.CSharp.Conversion.IsThrow.get -> bool Microsoft.CodeAnalysis.CSharp.Conversion.IsTupleConversion.get -> bool Microsoft.CodeAnalysis.CSharp.Conversion.IsTupleLiteralConversion.get -> bool Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp7 = 7 -> Microsoft.CodeAnalysis.CSharp.LanguageVersion @@ -123,6 +124,12 @@ Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax.Identifier.get -> Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax.WithIdentifier(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.ThrowKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken throwKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.WithThrowKeyword(Microsoft.CodeAnalysis.SyntaxToken throwKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Name.get -> Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Type.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax @@ -182,6 +189,7 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.ParenthesizedVariableDesignation = 8931 Microsoft.CodeAnalysis.CSharp.SyntaxKind.RefExpression = 9050 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.RefType = 9051 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleVariableDesignation = 8930 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.ThrowExpression = 9052 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.TupleElement = 8926 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.TupleExpression = 8927 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.TupleType = 8925 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind @@ -207,6 +215,7 @@ override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitParenthesizedVa override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitRefExpression(Microsoft.CodeAnalysis.CSharp.Syntax.RefExpressionSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitRefType(Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitSingleVariableDesignation(Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitThrowExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitTupleElement(Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitTupleExpression(Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitTupleType(Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode @@ -253,6 +262,8 @@ override Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax.Accept(Microsoft.Cod override Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void @@ -296,6 +307,8 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RefExpression(Microsoft.CodeA static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RefType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RefType(Microsoft.CodeAnalysis.SyntaxToken refKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SingleVariableDesignation(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ThrowExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ThrowExpression(Microsoft.CodeAnalysis.SyntaxToken throwKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TupleElement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TupleElement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax name) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TupleExpression(Microsoft.CodeAnalysis.SeparatedSyntaxList arguments = default(Microsoft.CodeAnalysis.SeparatedSyntaxList)) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax @@ -320,6 +333,7 @@ virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitParenthesizedVari virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitRefExpression(Microsoft.CodeAnalysis.CSharp.Syntax.RefExpressionSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitRefType(Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSingleVariableDesignation(Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitThrowExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleElement(Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleExpression(Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleType(Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax node) -> void @@ -339,6 +353,7 @@ virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitParenthe virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitRefExpression(Microsoft.CodeAnalysis.CSharp.Syntax.RefExpressionSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitRefType(Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSingleVariableDesignation(Microsoft.CodeAnalysis.CSharp.Syntax.SingleVariableDesignationSyntax node) -> TResult +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitThrowExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleElement(Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleExpression(Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTupleType(Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax node) -> TResult diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index e140dc0daa4de5cb3861b27448786f54751e5e2c..4dc648102fff8698f3923a8c8b3dbcd11822d4f8 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -1693,6 +1693,13 @@ Creates an IsPatternExpressionSyntax node. + + + + + + + diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index a9956d6415100eface5d9123fa8afd88ec5b9ed5..cfddab9d09339d8ed3d70148153eefa9443aa05d 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -561,5 +561,6 @@ public enum SyntaxKind : ushort DeclarationExpression = 9040, RefExpression = 9050, RefType = 9051, + ThrowExpression = 9052, } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs index 4fe2d403856573e52f834236d87de9b4f85b28c0..a432815d44102cbd4501a81507cc6385891592c5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs @@ -14713,5 +14713,223 @@ public static void Main() Diagnostic(ErrorCode.ERR_BadUnaryOp, "(1, null) is Program").WithArguments("is", "(int, )").WithLocation(6, 13) ); } + + [Fact] + public void ThrowExpressionForParameterValidation() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + foreach (var s in new[] { ""0123"", ""foo"" }) + { + Console.Write(s + "" ""); + try + { + Console.WriteLine(Ver(s)); + } + catch (ArgumentException) + { + Console.WriteLine(""throws""); + } + } + } + static int Ver(string s) + { + var result = int.TryParse(s, out int k) ? k : throw new ArgumentException(nameof(s)); + return k; // definitely assigned! + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: +@"0123 123 +foo throws"); + } + + [Fact] + public void ThrowExpressionWithNullable01() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + Console.WriteLine(M(1)); + try + { + Console.WriteLine(M(null)); + } + catch (Exception) + { + Console.WriteLine(""thrown""); + } + } + static int M(int? data) + { + return data ?? throw null; + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: +@"1 +thrown"); + } + + [Fact] + public void ThrowExpressionWithNullable02() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + Console.WriteLine(M(1)); + try + { + Console.WriteLine(M(null)); + } + catch (Exception) + { + Console.WriteLine(""thrown""); + } + } + static string M(object data) + { + return data?.ToString() ?? throw null; + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: +@"1 +thrown"); + } + + [Fact] + public void ThrowExpressionWithNullable03() + { + var source = +@"using System; +using System.Threading.Tasks; + +class Program +{ + public static void Main(string[] args) + { + MainAsync().Wait(); + } + static async Task MainAsync() + { + foreach (var i in new[] { 1, 2 }) + { + try + { + var used = (await Foo(i))?.ToString() ?? throw await Bar(i); + } + catch (Exception ex) + { + Console.WriteLine(""thrown "" + ex.Message); + } + } + } + static async Task Foo(int i) + { + await Task.Yield(); + return (i == 1) ? i : (object)null; + } + static async Task Bar(int i) + { + await Task.Yield(); + Console.WriteLine(""making exception "" + i); + return new Exception(i.ToString()); + } +} +"; + var compilation = CreateCompilation(source, options: TestOptions.DebugExe, + references: new[] { MscorlibRef_v4_0_30316_17626, SystemRef_v4_0_30319_17929, SystemCoreRef_v4_0_30319_17929 }); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: +@"making exception 2 +thrown 2"); + } + + [Fact] + public void ThrowExpressionPrecedence01() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + Exception ex = null; + try + { + // The ?? operator is right-associative, even under 'throw' + ex = ex ?? throw ex ?? throw new ArgumentException(""blue""); + } + catch (ArgumentException x) + { + Console.WriteLine(x.Message); + } + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: +@"blue"); + } + + [Fact] + public void ThrowExpressionPrecedence02() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + MyException ex = null; + try + { + // Throw expression binds looser than + + ex = ex ?? throw ex + 1; + } + catch (MyException x) + { + Console.WriteLine(x.Message); + } + } +} +class MyException : Exception +{ + public MyException(string message) : base(message) {} + public static MyException operator +(MyException left, int right) + { + return new MyException(""green""); + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: +@"green"); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs index a4243272f658509be570d529c08bb91b04e38c21..6e001b6f57061e3a721c18203d760c61381bfdb6 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs @@ -7,7 +7,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { [CompilerTrait(CompilerFeature.Patterns)] - public class PatternParsingTexts : CSharpTestBase + public class PatternParsingTexts : ParsingTests { [Fact] public void CasePatternVersusFeatureFlag() @@ -42,5 +42,163 @@ public static void Main(string[] args) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "args[0] is string s").WithArguments("pattern matching", "7").WithLocation(15, 18) ); } + + [Fact] + public void ThrowExpression_Good() + { + var test = @"using System; +class C +{ + public static void Sample(bool b, string s) + { + void NeverReturnsFunction() => throw new NullReferenceException(); + int x = b ? throw new NullReferenceException() : 1; + x = b ? 2 : throw new NullReferenceException(); + s = s ?? throw new NullReferenceException(); + NeverReturnsFunction(); + throw new NullReferenceException() ?? throw new NullReferenceException() ?? throw null; + } + public static void NeverReturns() => throw new NullReferenceException(); +}"; + CreateCompilationWithMscorlib(test).VerifyDiagnostics(); + CreateCompilationWithMscorlib(test, parseOptions: TestOptions.Regular6).VerifyDiagnostics( + // (6,14): error CS8059: Feature 'local functions' is not available in C# 6. Please use language version 7 or greater. + // void NeverReturnsFunction() => throw new NullReferenceException(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "NeverReturnsFunction").WithArguments("local functions", "7").WithLocation(6, 14), + // (6,40): error CS8059: Feature 'throw expression' is not available in C# 6. Please use language version 7 or greater. + // void NeverReturnsFunction() => throw new NullReferenceException(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "throw new NullReferenceException()").WithArguments("throw expression", "7").WithLocation(6, 40), + // (7,21): error CS8059: Feature 'throw expression' is not available in C# 6. Please use language version 7 or greater. + // int x = b ? throw new NullReferenceException() : 1; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "throw new NullReferenceException()").WithArguments("throw expression", "7").WithLocation(7, 21), + // (8,21): error CS8059: Feature 'throw expression' is not available in C# 6. Please use language version 7 or greater. + // x = b ? 2 : throw new NullReferenceException(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "throw new NullReferenceException()").WithArguments("throw expression", "7").WithLocation(8, 21), + // (9,18): error CS8059: Feature 'throw expression' is not available in C# 6. Please use language version 7 or greater. + // s = s ?? throw new NullReferenceException(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "throw new NullReferenceException()").WithArguments("throw expression", "7").WithLocation(9, 18), + // (11,47): error CS8059: Feature 'throw expression' is not available in C# 6. Please use language version 7 or greater. + // throw new NullReferenceException() ?? throw new NullReferenceException() ?? throw null; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "throw new NullReferenceException() ?? throw null").WithArguments("throw expression", "7").WithLocation(11, 47), + // (11,85): error CS8059: Feature 'throw expression' is not available in C# 6. Please use language version 7 or greater. + // throw new NullReferenceException() ?? throw new NullReferenceException() ?? throw null; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "throw null").WithArguments("throw expression", "7").WithLocation(11, 85), + // (13,42): error CS8059: Feature 'throw expression' is not available in C# 6. Please use language version 7 or greater. + // public static void NeverReturns() => throw new NullReferenceException(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "throw new NullReferenceException()").WithArguments("throw expression", "7").WithLocation(13, 42) + ); + } + + [Fact] + public void ThrowExpression_Bad() + { + var test = @"using System; +class C +{ + public static void Sample(bool b, string s) + { + // throw expression at wrong precedence + s = s + throw new NullReferenceException(); + if (b || throw new NullReferenceException()) { } + + // throw expression where not permitted + var z = from x in throw new NullReferenceException() select x; + M(throw new NullReferenceException()); + throw throw null; + (int, int) w = (1, throw null); + return throw null; + } + static void M(string s) {} +}"; + CreateCompilationWithMscorlib(test).VerifyDiagnostics( + // (7,17): error CS1525: Invalid expression term 'throw' + // s = s + throw new NullReferenceException(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "throw new NullReferenceException()").WithArguments("throw").WithLocation(7, 17), + // (8,18): error CS1525: Invalid expression term 'throw' + // if (b || throw new NullReferenceException()) { } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "throw new NullReferenceException()").WithArguments("throw").WithLocation(8, 18), + // (11,27): error CS8115: A throw expression is not allowed in this context. + // var z = from x in throw new NullReferenceException() select x; + Diagnostic(ErrorCode.ERR_ThrowMisplaced, "throw").WithLocation(11, 27), + // (12,11): error CS8115: A throw expression is not allowed in this context. + // M(throw new NullReferenceException()); + Diagnostic(ErrorCode.ERR_ThrowMisplaced, "throw").WithLocation(12, 11), + // (13,15): error CS8115: A throw expression is not allowed in this context. + // throw throw null; + Diagnostic(ErrorCode.ERR_ThrowMisplaced, "throw").WithLocation(13, 15), + // (14,9): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported + // (int, int) w = (1, throw null); + Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(int, int)").WithArguments("System.ValueTuple`2").WithLocation(14, 9), + // (14,28): error CS8115: A throw expression is not allowed in this context. + // (int, int) w = (1, throw null); + Diagnostic(ErrorCode.ERR_ThrowMisplaced, "throw").WithLocation(14, 28), + // (14,24): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported + // (int, int) w = (1, throw null); + Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(1, throw null)").WithArguments("System.ValueTuple`2").WithLocation(14, 24), + // (15,16): error CS8115: A throw expression is not allowed in this context. + // return throw null; + Diagnostic(ErrorCode.ERR_ThrowMisplaced, "throw").WithLocation(15, 16), + // (14,9): warning CS0162: Unreachable code detected + // (int, int) w = (1, throw null); + Diagnostic(ErrorCode.WRN_UnreachableCode, "(").WithLocation(14, 9) + ); + } + + [Fact] + public void ThrowExpression() + { + var tree = UsingTree(@" +class C +{ + int x = y ?? throw null; +}", options: TestOptions.Regular); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.ThrowExpression); + { + N(SyntaxKind.ThrowKeyword); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } } } diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 2fea3168d7a189ddb5007f3974fb2ae5cabf8a5f..a6a640dd722840d51614b6403d8a76186a7c4413 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -4348,8 +4348,7 @@ class C expr: "throw new System.Exception()", resultProperties: out resultProperties, error: out error); - - Assert.Equal("error CS1525: Invalid expression term 'throw'", error); + Assert.Equal("error CS8115: A throw expression is not allowed in this context.", error); } [WorkItem(1016555, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1016555")] diff --git a/src/Test/Utilities/Desktop/TestResource.resx b/src/Test/Utilities/Desktop/TestResource.resx index 72280b8698cc7217363d2a21dc90a109735d2dfd..419a5de546e192898384a54b0ed64223784f1c6a 100644 --- a/src/Test/Utilities/Desktop/TestResource.resx +++ b/src/Test/Utilities/Desktop/TestResource.resx @@ -331,6 +331,7 @@ namespace My { ++i; if (i is int q) continue; + void LocalFcn() => throw null; break; } do