Skip to content

Commit

Permalink
Merge pull request #13750 from dotnet/features/throwexpr
Browse files Browse the repository at this point in the history
Merge the feature 'throw expressions' into master
Fixes #13745.
  • Loading branch information
gafter committed Sep 20, 2016
2 parents 93a22e3 + 00aea7a commit d710efa
Show file tree
Hide file tree
Showing 32 changed files with 644 additions and 18 deletions.
15 changes: 8 additions & 7 deletions docs/Language Feature Status.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>](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) |
Expand All @@ -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
Updated 2016-08-29
30 changes: 30 additions & 0 deletions docs/features/throwexpr.md
Original file line number Diff line number Diff line change
@@ -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.
50 changes: 50 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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<ArgumentSyntax> arguments = node.Arguments;
Expand Down
9 changes: 7 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

/// <summary>
/// Binds an expression-bodied member with expression e as either { return e;} or { e; }.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -441,6 +443,17 @@ public bool IsEnumeration
}
}

/// <summary>
/// Returns true if the conversion is an implicit throw conversion.
/// </summary>
public bool IsThrow
{
get
{
return Kind == ConversionKind.ImplicitThrow;
}
}

// TODO: update the language reference section number below.
/// <summary>
/// Returns true if the conversion is an interpolated string conversion.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal enum ConversionKind : byte
Identity,
ImplicitNumeric,
ImplicitEnumeration,
ImplicitThrow,
ImplicitTupleLiteral,
ImplicitTuple,
ExplicitTupleLiteral,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,9 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi
return interpolatedStringConversion;
}
break;

case BoundKind.ThrowExpression:
return Conversion.ImplicitThrow;
}

return Conversion.NoConversion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1581,6 +1581,10 @@
<Node Name="BoundWildcardPattern" Base="BoundPattern">
</Node>

<Node Name="BoundThrowExpression" Base="BoundExpression">
<Field Name="Expression" Type="BoundExpression" Null="disallow"/>
</Node>

<!-- The node is transformed into BoundLocal or BoundFieldAccess after inference -->
<Node Name="OutVariablePendingInference" Base="BoundExpression">
<!-- Type is not significant for this node type; always null -->
Expand Down
19 changes: 19 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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<TArgument, TResult>(OperationVisitor<TArgument, TResult> 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)
Expand Down
8 changes: 8 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/Formatting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d710efa

Please sign in to comment.