{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/1340","repository_url":"https://api.github.com/repos/dotnet/csharplang","labels_url":"https://api.github.com/repos/dotnet/csharplang/issues/1340/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/csharplang/issues/1340/comments","events_url":"https://api.github.com/repos/dotnet/csharplang/issues/1340/events","html_url":"https://github.com/dotnet/csharplang/issues/1340","id":300053666,"number":1340,"title":"Proposal: Rethrow Expressions","user":{"login":"Nihlus","id":2340428,"avatar_url":"https://avatars0.githubusercontent.com/u/2340428?v=4","gravatar_id":"","url":"https://api.github.com/users/Nihlus","html_url":"https://github.com/Nihlus","followers_url":"https://api.github.com/users/Nihlus/followers","following_url":"https://api.github.com/users/Nihlus/following{/other_user}","gists_url":"https://api.github.com/users/Nihlus/gists{/gist_id}","starred_url":"https://api.github.com/users/Nihlus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Nihlus/subscriptions","organizations_url":"https://api.github.com/users/Nihlus/orgs","repos_url":"https://api.github.com/users/Nihlus/repos","events_url":"https://api.github.com/users/Nihlus/events{/privacy}","received_events_url":"https://api.github.com/users/Nihlus/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":7,"created_at":"2018-02-25T19:31:36Z","updated_at":"2018-02-25T22:32:33Z","closed_at":null,"author_association":"NONE","body":"# Rethrow Expressions\r\n\r\n* [x] Proposed\r\n* [ ] Prototype: [None](https://github.com/PROTOTYPE_OWNER/roslyn/BRANCH_NAME)\r\n* [ ] Implementation: [None](https://github.com/dotnet/roslyn/BRANCH_NAME)\r\n* [ ] Specification: [Not Started](pr/1)\r\n\r\n## Summary\r\n[summary]: #summary\r\n\r\nIn C# 7.0, `throw` expressions were introduced. This proposal outlines a simple  extension to the existing syntax changes, enabling not only `throw x` as an expression, but `throw` (in the context of rethrowing a caught exception) as well.\r\n\r\n## Motivation\r\n[motivation]: #motivation\r\n\r\nIn the current iteration of the language, `throw` expressions are widely used to conditionally select either a value or expression, or throwing an exception. The syntax that is already in place allows simple and terse usage of these patterns, however, in its design one pattern was left behind: `throw` without an argument, inside a `catch` block. \r\n\r\nEnabling its use in similar fashion to the C# 7.0 throw expressions fills in the gap left behind, and completes the expected syntax for `throw` expressions. Furthermore, it makes it far easier for developers using the new syntax to utilize it in a way that preserves stack traces of caught exceptions.\r\n\r\n## Detailed design\r\n[design]: #detailed-design\r\n\r\nIn principle, this proposal is a simple extension to exception rethrowing syntax when used in combination with null-coalescing and ternary operators. At present, the available syntax when combining the two is somewhat limited, but presents an opportunity for a small yet useful improvement.\r\n\r\n\r\n### Background\r\n[background]: #background\r\n\r\nFirst, some background. If you consider yourself versed in C# exception handling, you can skip ahead to [Proposal](#proposal).\r\n\r\nWhen exceptions are thrown and caught in the language using `try`/`catch` blocks, exceptions can be swallowed and/or handled, rethrown, or new exceptions can be thrown. Depending on the syntax used, the stack trace of the exception is preserved or destroyed.\r\n\r\nExceptions can also contain inner exceptions, which are defined to be the cause of the outer exception. A commonly seen use of this is in the `TargetInvocationException` class, which automatically wraps exceptions thrown in constructors.\r\n\r\nSome examples of the various ways exceptions can be manipulated in these ways:\r\n\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    // handle the exception cleanly\r\n}\r\n```\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    throw; // rethrow the exception, preserving the stack trace\r\n}\r\n```\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    throw ex; // rethrow the exception, destroying the stack trace and creating a new one from this point\r\n}\r\n```\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    throw ex.InnerException; // rethrow the inner exception, creating a new stack trace from this point\r\n}\r\n```\r\n\r\nOf note is also a side effect when the operand to the `throw` keyword is null, which is technically valid, that produces a thrown `NullReferenceException`. It is important to be aware of this behaviour in the context of this proposal, and in normal code. \r\n\r\nParticularly, in the example above, if no inner exception is present, a `NullReferenceException` will be thrown instead, whereas it may be preferable to handle the lack of an inner exception differently (either by rethrowing the outer exception directly, or cleanly handling the case where no inner exception is present). This behaviour, while typically avoided in robust code, is within the scope of this proposal as a factor for simplifying its avoidance.\r\n\r\nAs a final example, the behaviour can be explicitly triggered using the following pattern:\r\n\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    throw null;\r\n}\r\n```\r\n\r\nThe language, in its current iteration, has a method for handling the not null/null case when dealing with instances of objects: the null coalescing operator. This operator is a quick shorthand for inspecting a reference, determining if it is null, and selecting either the original reference or an alternative value in the case of null.\r\n\r\n```csharp\r\nvar value = possiblyNull ?? alternativeValue;\r\n```\r\nIn the compiler, this is lowered into a simple if statement (example lowered code may not be exactly accurate, but near enough for the core concept).\r\n\r\n```csharp\r\nvar value = possiblyNull;\r\nif (value is null)\r\n{\r\n    value = alternativeValue;\r\n}\r\n```\r\nExceptions, being instances of objects, are subject to the same syntax. '\r\n\r\nAn extension to their use in null-coalescing operators was made in C# 7.0, wherein new syntax was introduced that considers the `throw x` syntax as an expression, allowing it to be used as the right-hand operand in null-coalescing operators, that is, \r\n\r\n```csharp\r\nvar value = possiblyNull ?? throw new RelevantException();\r\n```\r\n`throw` expressions may also appear as the left-hand operator in null-coalescing statements, but can only be combined with either a value, or another throw expression with an exception instance argument.\r\n\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    throw ex.InnerException ?? throw ex;\r\n}\r\n```\r\n\r\nOther than null-coalescing operators, there is also the ternary operator, which accepts throw expressions in a similar fashion.\r\n\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    return condition ? value : throw ex;\r\n}\r\n```\r\n\r\n### Proposal\r\n[proposal]: #proposal\r\n\r\nI propose an extension to the existing throw expression syntax, allowing `throw`, in the context of a `catch` block and without an argument, to be treated as an expression. \r\n\r\nWith the extension, throw expression, ternary- and null-coalescing operator syntax is available that allows their combination into terse constructs.\r\n\r\nOne, a construct that throws a given expression object if it is not null, and rethrows the existing exception if it is, *preserving the stack trace*.\r\n\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    throw ex.InnerException ?? throw;\r\n}\r\n```\r\nThis construct, while not currently valid syntax, is a logical extension to existing solutions and follows an easily readable format.\r\n\r\nIn particular, this would be a useful shorthand for unwrapping exceptions with potential causational exceptions, where the developer would want to preserve the stack trace in the case of a missing inner exception, and begin a new one if not.\r\n\r\nPractically, this change would be applicable not only to exception unwrapping, but also to more general applications, in similar vein to the current throw expressions.\r\n\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    var value = possiblyNull ?? throw;\r\n}\r\n```\r\n\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    return condition ? value : throw;\r\n}\r\n```\r\n\r\nEffectively, this means a few things:\r\n\r\n1. `throw` is now considered an expression, and can now be used as the right-hand operator in a null-coalescing construct.\r\n2. In null-coalescing constructs, if the left-hand operator is a `throw new` expression, its target exception reference is checked for null, and if it is null, the right throw expression is selected. If not, the left throw expression is selected. This is already the existing behaviour, and simply needs to be extended to accept 1.\r\n3. In ternary conditionals, if the left-hand operator is an and the right-hand operator is a `throw`, the expression is either chosen, or the current exception is rethrown.\r\n\r\n### Implementation\r\n[implementation]: #implementation\r\n\r\nIn similar fashion to the existing implementation of the null-coalescing operator, the construct can be implemented using simple compiler lowering. The above examples would, after lowering, be transformed into C# syntax valid in the current language version.\r\n\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    if (ex.InnerException is null) // or == null, if operator overloading semantics are to be preserved\r\n    {\r\n         throw;\r\n    }\r\n\r\n    throw ex.InnerException;\r\n}\r\n```\r\n\r\n```csharp\r\ntry\r\n{\r\n    // some code that throws\r\n}\r\ncatch (Exception ex)\r\n{\r\n    if (conditon)\r\n    {\r\n         return value; // or whichever left-hand expression was passed to the ternary\r\n    }\r\n\r\n    throw;\r\n}\r\n```\r\n\r\n## Drawbacks\r\n[drawbacks]: #drawbacks\r\n\r\nI see no major drawbacks to implementing this proposal. It does not, as far as I can tell, render any existing constructs invalid or incompatible, nor does it alter existing behaviour.\r\n\r\nHowever, I'm certain there will be additonal considerations to take into account once the proposal has been explored further.\r\n\r\n## Alternatives\r\n[alternatives]: #alternatives\r\n\r\nAs discussed above, there are equivalent constructs already available to developers in the form of simple `if` conditionals. Much in the same way as ternaries and null-conditionals simplify code for developers, this proposal's intent is to simplify that very construct.\r\n\r\nHowever, for the sake of clarity, I will refer back to a previous section for the two most common examples of equivalent syntax - the null-coalescing operator, and the ternary return conditional. See [Implementation](#implementation).\r\n\r\n\r\n## Unresolved questions\r\n[unresolved]: #unresolved-questions\r\n\r\nI'm quite sure more applications for this new syntax (and more issues to consider) will rear their head once it's been explored further. What other instances can the new syntax be used in? What side effects could it have? How difficult will this lowering step be to implement?\r\n\r\nUnfortunately, I am not a compiler developer, and cannot answer most of these questions beyond what I have already outlined.\r\n\r\nAs such, thoughts, input, and suggestions are very welcome."},"comment":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/comments/368351444","html_url":"https://github.com/dotnet/csharplang/issues/1340#issuecomment-368351444","issue_url":"https://api.github.com/repos/dotnet/csharplang/issues/1340","id":368351444,"user":{"login":"jnm2","id":8040367,"avatar_url":"https://avatars2.githubusercontent.com/u/8040367?v=4","gravatar_id":"","url":"https://api.github.com/users/jnm2","html_url":"https://github.com/jnm2","followers_url":"https://api.github.com/users/jnm2/followers","following_url":"https://api.github.com/users/jnm2/following{/other_user}","gists_url":"https://api.github.com/users/jnm2/gists{/gist_id}","starred_url":"https://api.github.com/users/jnm2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jnm2/subscriptions","organizations_url":"https://api.github.com/users/jnm2/orgs","repos_url":"https://api.github.com/users/jnm2/repos","events_url":"https://api.github.com/users/jnm2/events{/privacy}","received_events_url":"https://api.github.com/users/jnm2/received_events","type":"User","site_admin":false},"created_at":"2018-02-25T22:32:33Z","updated_at":"2018-02-25T22:32:33Z","author_association":"CONTRIBUTOR","body":"I don't like the proposed syntax, and besides, you really do want to use `ExceptionDispatchInfo` rather than `throw <already-thrown instance>` and catch filters rather than `throw;`."}}
{"actor":{"display_login":"jnm2"}}