Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine deconstruction assignment and declaration, and support discards. #15548

Merged
merged 10 commits into from
Dec 12, 2016

Conversation

gafter
Copy link
Member

@gafter gafter commented Nov 27, 2016

  • Combine deconstruction assignment and declaration, and support discards.
  • Because tuples may contain declarations, adjust lambda disambiguation
    and adjust parsing of argument lists.
  • Diagnose tuple element names on the left of a deconstruction.
  • Add relational operators to disambiguating tokens in 7.5.4.2
  • Remove BoundVoid

Fixes #15548
Fixes #15549

Update (from jcouv): the bug numbers seem wrong. I think the correct ones were #14794
and #14832

/cc @jcouv @AlekseyTs @VSadov @dotnet/roslyn-compiler

- Combine deconstruction assignment and declaration, and support discards.
- Add wildcards.work.md to track outstanding work.
- Bind each type syntax once in a deconstruction.
- Because tuples may contain declarations, adjust lambda disambiguation
  and adjust parsing of argument lists.
- Diagnose tuple element names on the left of a deconstruction.
- Add relational operators to disambiguating tokens in 7.5.4.2
{
var pending = (BoundDiscardedExpression)expression;
if ((object)pending.Type == null)
{
Copy link
Member Author

@gafter gafter Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the caller has already checked this, I will remove the if and add an assertion that the type is null. #Resolved

@@ -719,14 +744,20 @@ private DeconstructionVariable BindDeconstructionVariables(ExpressionSyntax node
}
}

private BoundDiscardedExpression BindDiscardedExpression(
DiscardedDesignationSyntax designation,
TypeSymbol declType)
Copy link
Member

@cston cston Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static #Closed

}

// Bind a declaration variable where it isn't permitted.
private BoundExpression BindDeclarationVariables(TypeSymbol declType, VariableDesignationSyntax node, DiagnosticBag diagnostics)
Copy link
Member

@cston cston Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment correct? From the body of the method, it's not clear that the declaration variable isn't permitted. #ByDesign

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it is true that the code implements them, this is used only to bind them in contexts in which they are not currently permitted, via the method above which gives an error message. As you can see, the type inference (line 735 below) is short-circuited to produce an error type.


In reply to: 89843255 [](ancestors = 89843255)

builder.Add(BindDeclarationVariables(declType, n, diagnostics));
}
var subExpressions = builder.ToImmutableAndFree();
var tupleType = (TypeSymbol)this.Compilation.CreateTupleTypeSymbol(subExpressions.Select<BoundExpression, ITypeSymbol>(e => e.Type).ToImmutableArray());
Copy link
Member

@cston cston Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using TupleTypeSymbol directly rather than ITypeSymbol, etc.:

TupleTypeSymbol.Create(
    null,
    subExpressions.SelectAsArray(e => e.Type),
    default(ImmutableArray<Location>),
    default(ImmutableArray<string>,
    Compilation);
``` #Resolved

}
var subExpressions = builder.ToImmutableAndFree();
var tupleType = (TypeSymbol)this.Compilation.CreateTupleTypeSymbol(subExpressions.Select<BoundExpression, ITypeSymbol>(e => e.Type).ToImmutableArray());
return new BoundTupleLiteral(node, ImmutableArray<string>.Empty, subExpressions, tupleType);
Copy link
Member

@cston cston Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should argumentNamesOpt be default(ImmutableArray<string>) rather than Empty? #Resolved

// We only allow assignment-only or declaration-only deconstructions at this point.
// Issue https://github.com/dotnet/roslyn/issues/15050 tracks allowing mixed deconstructions.
// For now we give an error when you mix.
Error(diagnostics, ErrorCode.ERR_MixedDeconstructionUnsupported, left);
Copy link
Member

@cston cston Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test for this case. #Resolved

Copy link
Member Author

@gafter gafter Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There already is one: MixedDeconstruction_03 in DeconstructionTests.cs


In reply to: 89845635 [](ancestors = 89845635)

ERR_MixedDeconstructionUnsupported = 8184,
ERR_DeclarationExpressionNotPermitted = 8185,
ERR_MustDeclareForeachIteration = 8186,
ERR_TupleElementNamesInDeconstruction = 8187,
Copy link
Member

@cston cston Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add tests for each of these diagnostics. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ERR_MixedDeconstructionUnsupported: four tests in Emit\CodeGen\CodeGenDeconstructTests.cs, and two in Syntax\Parsing\DeconstructionTests.cs

for ERR_DeclarationExpressionNotPermitted: many tests in Emit\CodeGen\CodeGenTupleTest.cs, Semantic\Semantics\DeconstructionTests.cs, Semantic\Semantics\LocalFunctionTests.cs, Semantic\Semantics\OutVarTests.cs, and Semantic\Semantics\PatternSwitchTests.cs

for ERR_MustDeclareForeachIteration: two tests in Emit\CodeGen\CodeGenDeconstructTests.cs

for ERR_TupleElementNamesInDeconstruction: two tests in Emit\CodeGen\CodeGenDeconstructTests.cs

Are you saying that you don't think these tests are sufficient?


In reply to: 89845938 [](ancestors = 89845938)

Copy link
Member

@cston cston Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed those tests since the webpage did not include those in the diff. #Resolved

@@ -255,7 +277,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
hasErrors = hasErrors || iterationVariableType.IsErrorType();

// Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts).
if (hasErrors)
if (hasErrors || _syntax.HasErrors)
Copy link
Member

@cston cston Nov 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the check for _syntax.HasErrors necessary? What if the error is something simple such as missing in token? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with suppressing semantic errors in the face of syntax errors, given that we've bound all the pieces of the syntax needed for a good IDE/SemanticModel experience.


In reply to: 89846888 [](ancestors = 89846888)

Copy link
Member Author

@gafter gafter Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change the syntax errors check to check for syntax errors only on the declaration part. #Resolved

This is now a semantic restriction (until we decide to remove it).
return (deconstruction == null)
? (CommonForEachStatementSyntax)_syntaxFactory.ForEachStatement(@foreach, openParen, type, name, @in, expression, closeParen, statement)
: (CommonForEachStatementSyntax)_syntaxFactory.ForEachVariableStatement(@foreach, openParen, deconstruction, @in, expression, closeParen, statement);
private bool IsValidForeachVariable(ExpressionSyntax variable)
Copy link
Member

@cston cston Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private [](start = 8, length = 7)

static #Resolved

// differently, as we check for the comma before concluding that the identifier should cause a
// disambiguation. For example, for the input `(A < B , C > D)`, we treat this as a tuple with
// two elements, because if we considered the `A<B,C>` to be a type, it wouldn't be a tuple at
// all. Since we don't have such as thing as a one-element tuple (even for deconstruction), the
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

such as thing [](start = 36, length = 13)

Typo "such as thing" vs. "such a thing"? #Closed

}

// check for a designation
if (!ScanDesignation(permitTupleDesignation && (lastTokenOfType.IsVar() || IsPredefinedType(lastTokenOfType.Kind))))
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lastTokenOfType.IsVar() [](start = 64, length = 23)

Will this condition do the right thing for a type name "A.var"? #Closed

Copy link
Member Author

@gafter gafter Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not, as we'll want A.var(x, y) to be an invocation expression. I'll revise it. #Resolved

return false;
case SyntaxKind.IdentifierToken:
var result = this.IsTrueIdentifier();
this.EatToken();
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.EatToken(); [](start = 20, length = 16)

It doesn't feel appropriate to eat the token when we are about to return false. #Closed

Copy link
Member Author

@gafter gafter Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we return false, the caller unwinds the trial parse, so it doesn't matter. #ByDesign

Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be cleaner and more intuitive if this function (and the like) wouldn't consume tokens it doesn't accept. #Closed

Copy link
Member Author

@gafter gafter Nov 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That isn't possible given the way we scan forward and discard tokens before making a decision. Even if we don't eat the very last token, we will have eaten preceding tokens that we don't accept. Avoiding eating the very last token would be extra work that serves no purpose. #ByDesign

switch (this.CurrentToken.Kind)
{
case SyntaxKind.CloseParenToken:
this.EatToken();
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.EatToken(); [](start = 32, length = 16)

It doesn't feel appropriate to eat the token when we are about to return false. #Closed

Copy link
Member Author

@gafter gafter Nov 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with other scan methods, it is unspecified what tokens have been eaten when it returns false. #ByDesign

@@ -5346,6 +5343,13 @@ private bool IsTrueIdentifier()
return false;
}

private bool IsTrueIdentifier(SyntaxToken token)
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsTrueIdentifier [](start = 21, length = 16)

Should the method above delegate its work to this method? Otherwise, the methods have the same name, but appear to use different criteria. #Closed

Copy link
Member Author

@gafter gafter Nov 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, but not quite so simple to do, as there are a series of helper methods for the previous method intended to help it disambiguate, implicitly based on the current token in declarations; it peeks forward using PeekToken(int) and even does some trial forward parsing.

This method is for use in executable code (not declarations), so it has a much simpler implementation that depends only on the token passed in and this.IsInQuery. I'll add some clarifying doc comments. #Resolved

if ((options & NameOptions.AfterIsOrCaseOrOutOrTupleComma) != 0 ||
(options & NameOptions.FirstElementOfPossibleTupleLiteral) != 0 && this.PeekToken(1).Kind == SyntaxKind.CommaToken)
{
// we allow 'G<T,U> x' as a pattern-matching operation and a declaration pattern in a tuple.
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

declaration pattern [](start = 85, length = 20)

Typo "declaration pattern in a tuple"? #Closed

var type = this.ParseUnderlyingType(parentIsParameter: mode == ParseTypeMode.Parameter, options: nameOptions);

if (this.CurrentToken.Kind == SyntaxKind.QuestionToken &&
(mode != ParseTypeMode.AfterIsOrCase || !IsTrueIdentifier(this.PeekToken(1))))
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(mode != ParseTypeMode.AfterIsOrCase || !IsTrueIdentifier(this.PeekToken(1)))) [](start = 16, length = 78)

Consider adding a comment explaining the rational behind this new condition. #Closed

if (tk == SyntaxKind.RefKeyword ||
(SyntaxFacts.IsPredefinedType(tk) && this.PeekToken(1).Kind != SyntaxKind.DotToken) || IsDeclarationModifier(tk))
if (tk == SyntaxKind.RefKeyword || IsDeclarationModifier(tk) ||
(SyntaxFacts.IsPredefinedType(tk) && this.PeekToken(1).Kind != SyntaxKind.DotToken && this.PeekToken(1).Kind != SyntaxKind.OpenParenToken))
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.PeekToken(1).Kind != SyntaxKind.OpenParenToken [](start = 102, length = 51)

Consider adding a clarifying comment for this condition. #Closed

var assignment = (AssignmentExpressionSyntax)expression;
if (assignment.Left.IsDeconstructionDeclarationLeft())
{
statement = this.AddError(statement, ErrorCode.ERR_BadEmbeddedStmt);
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

statement = this.AddError(statement, ErrorCode.ERR_BadEmbeddedStmt); [](start = 28, length = 68)

It is not clear why this error is no longer reported. Was there an explicit LDM decision? #Closed

Copy link
Member Author

@gafter gafter Nov 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the LDM explicitly decided to remove the language concept of a deconstruction declaration statement, which is where this restriction arises. We will discuss tomorrow if the LDM wants to add back any of these restrictions in C# 7, including the ones among the first changes in Binder_Deconstruct.cs (lines 26-57 in this PR); as of the last LDM they wanted to remove all those restrictions. But when we do we will need additional tests. #ByDesign

Copy link
Contributor

@AlekseyTs AlekseyTs Nov 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I read the response, and based on my recollection of the LDM discussion, the answer to my question "Was there an explicit LDM decision?" is No. Meaning the LDM did not make an explicit decision about this error. So, let's hold on on removing it until the decision is made. #Closed

Copy link
Member Author

@gafter gafter Nov 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You misread the response. We did discuss the error, I raised the point that it is no longer an error if it is not a declaration when in the position of a controlled statement, and that was OK with Mads and others. If the LDM explicitly decides to change their mind and re-introduce an error here, I will do that. #Resolved

Copy link
Member Author

@gafter gafter Dec 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LDM met yesterday and reconfirmed this. From the raw LDM notes:

Declarations in embedded statements?

Let's not add errors for deconstruction situations. In principle we probably want to remove even the existing limitation, but it's work we won't push for in C# 7.0.
#Resolved

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Nov 29, 2016

        Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForKeyword || this.CurrentToken.Kind == SyntaxKind.ForEachKeyword);

Should this condition be now removed? #Closed


Refers to: src/Compilers/CSharp/Portable/Parser/LanguageParser.cs:7832 in 1003470. [](commit_id = 1003470, deletion_comment = False)

@@ -7812,28 +7844,22 @@ private StatementSyntax ParseForOrForEachStatement()
var resetPoint = this.GetResetPoint();
try
{
if (this.CurrentToken.Kind == SyntaxKind.ForKeyword)
Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForKeyword);
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForKeyword); [](start = 16, length = 62)

It looks like we already asserted this at the beginning of this function. #Closed

this.CurrentToken.Kind == SyntaxKind.CloseBracketToken)
{
// convert `]` into `)` or vice versa for error recovery
closeToken = this.ConsumeOneToken(closeKind);
Copy link
Member

@cston cston Nov 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.ConsumeOneToken(closeKind) [](start = 33, length = 31)

Why consume the ']' or ')' if it's not the close kind expected? Is it more likely the user closed with the wrong kind or that the expected kind is missing? #Resolved

Copy link
Member Author

@gafter gafter Nov 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently parse a C++-style declaration
Type x(args, ...)
using the bracketed argument list, which our syntax trees support. If we don't turn the ( into a [, then we get out of sync and will think the argument list is a tuple literal. And then we won't parse out variable declarations properly. Doing the same substitution for the close paren reduces cascaded diagnostics. #Resolved

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 7, 2016

<Field Name="Variable" Type="ExpressionSyntax">

Should the list of kinds within this element be adjusted? #Closed


Refers to: src/Compilers/CSharp/Portable/Syntax/Syntax.xml:2120 in c4d4991. [](commit_id = c4d4991, deletion_comment = False)

@@ -99,7 +99,8 @@ protected override ImmutableArray<LocalSymbol> BuildLocals()
case SyntaxKind.IdentifierName:
break;
default:
throw ExceptionUtilities.UnexpectedValue(declaration.Kind());
ExpressionVariableFinder.FindExpressionVariables(this, locals, declaration);
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExpressionVariableFinder.FindExpressionVariables(this, locals, declaration); [](start = 20, length = 76)

Could you please add a comment about the scenario when we get here and point me to the test(s) that covers this line of code? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. The tests would be UnitTests.CodeGen.CodeGenDeconstructTests.MixedDeconstruction_06, UnitTests.CodeGen.CodeGenDeconstructTests.ForeachIntoExpression, and UnitTests.CodeGen.CodeGenDeconstructTests.MixedDeconstruction_05. Comments in the next iteration.


In reply to: 91385737 [](ancestors = 91385737)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 7, 2016

            // for error recovery, we parse `int (x, y) = e` as a deconstruction.

Should we keep the comment, is it still valid? #Closed


Refers to: src/Compilers/CSharp/Portable/Parser/LanguageParser.cs:9220 in 1003470. [](commit_id = 1003470, deletion_comment = True)

// absence of the comma after the `D` means we don't treat the `D` as contributing to the
// disambiguation of the expression/type. More formally, ...
//
// If a sequence of tokens can be parsed(in context) as a* simple-name* (§7.6.3), *member-access* (§7.6.5),
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

member-access [](start = 90, length = 15)

Consider removing the ** annotations throughout this comment. #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to keep the markup from the specification.


In reply to: 91392143 [](ancestors = 91392143)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a* simple-name* intended markup, note position of the first *? There are several similar places throughout this comment.


In reply to: 91453230 [](ancestors = 91453230,91392143)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is what is in the draft spec. I agree it could be improved ever so slightly. If this is what is holding up your approval of the code review, I think we should hold this set of changes until I am back in the office in January.


In reply to: 91570733 [](ancestors = 91570733,91453230,91392143)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not block on the markup here.

return result;
if ((object)this._type == null)
{
diagnostics.Add(this.ForbiddenDiagnostic, Locations[0], Name);
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diagnostics.Add(this.ForbiddenDiagnostic, Locations[0], Name); [](start = 20, length = 62)

It is not clear why ForbiddenDiagnostic is an appropriate diagnostics to use in this case. Also, could you please point me to the test(s) covering this line? #Closed

Copy link
Member Author

@gafter gafter Dec 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know of a way to trigger this defensive code. I'll remove it and let the assertion in the caller fail if it (_type == null) occurs.


In reply to: 91395018 [](ancestors = 91395018)

{
diagnostics.Add(this.ForbiddenDiagnostic, Locations[0], Name);
SetType(new ExtendedErrorTypeSymbol(
((SourceModuleSymbol)this.ContainingModule).DeclaringCompilation, name: "var", arity: 0, errorInfo: null, variableUsedBeforeDeclaration: true));
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

variableUsedBeforeDeclaration: true) [](start = 130, length = 36)

Why does it make sense to set variableUsedBeforeDeclaration to true here and what effect is this going to have? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't have any effect other than recording that the variable's type was required at a location before it was able to be inferred.


In reply to: 91395381 [](ancestors = 91395381)

Debug.Assert((object)this._type != null);
if ((object)this._type == null)
{
diagnostics.Add(this.ForbiddenDiagnostic, Locations[0], Name);
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diagnostics.Add(this.ForbiddenDiagnostic, Locations[0], Name); [](start = 20, length = 62)

It is not clear why ForbiddenDiagnostic is an appropriate diagnostics to use in this case. Also, could you please point me to the test(s) covering this line? #Closed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels that ERR_TypeInferenceFailedForImplicitlyTypedOutVariable or the like would be more appropriate in this case. Also, it looks like there is no guarantee that this diagnostics is ever going to be surfaced to the user. It would be good to enforce that we are going to get here only for broken code (have syntax errors or something). I would prefer a throw, but an assert should be fine as well.


In reply to: 91395620 [](ancestors = 91395620)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, diagnostics from this method are always discarded. I'll remove it.

This is covered by OutVarTests.OutVarDeconstruction_02

There isn't a mechanism to "enforce" that we only get here for broken code, because we don't have the necessary compilation diagnostics in hand. Ideas for doing that are welcome.


In reply to: 91397639 [](ancestors = 91397639,91395620)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't a mechanism to "enforce" that we only get here for broken code, because we don't have the necessary compilation diagnostics in hand. Ideas for doing that are welcome.

I believe we should never get here for an Out Var or a pattern variable. It should be fairly easy to detect such cases by examining the syntax. Please add the detection and a throw for them.


In reply to: 91455715 [](ancestors = 91455715,91397639,91395620)

@@ -7246,7 +7246,7 @@ public void M1()
parseOptions: TestOptions.Regular);
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/15508"), Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)]
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skip = "#15508" [](start = 14, length = 54)

It is fine to merge with skipped test into a feature branch, but I think we should come up with solution or implement a workaround that prevents the crash before we merge this to master. #Closed

Copy link
Member

@jaredpar jaredpar Dec 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. This is an ask mode change to RC. We can't introduce new IDE regressions here without explicit IDE buy off. Otherwise this will not pass the ask mode review. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That (not crashing) was the reason for the addition of "return false" in "CSharpTypeInferenceService.TypeInferrer.cs".

I'll adjust this test so it reflects the "broken" but non-crashing behavior.


In reply to: 91455957 [](ancestors = 91455957)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 8, 2016

Done with review pass. A number of active comments remains, I think I reactivated a several existing threads as well. #Closed

@gafter
Copy link
Member Author

gafter commented Dec 8, 2016

<Field Name="Variable" Type="ExpressionSyntax">

I will remove the list.


In reply to: 265570868 [](ancestors = 265570868)


Refers to: src/Compilers/CSharp/Portable/Syntax/Syntax.xml:2120 in c4d4991. [](commit_id = c4d4991, deletion_comment = False)

@gafter
Copy link
Member Author

gafter commented Dec 8, 2016

            // for error recovery, we parse `int (x, y) = e` as a deconstruction.

Yes.


In reply to: 265577755 [](ancestors = 265577755)


Refers to: src/Compilers/CSharp/Portable/Parser/LanguageParser.cs:9220 in 1003470. [](commit_id = 1003470, deletion_comment = True)

@gafter
Copy link
Member Author

gafter commented Dec 8, 2016

git repository authentication is broken again, so I can't push to the repository. When I can I'll send the next iteration, which addresses all comments. #Resolved

@gafter
Copy link
Member Author

gafter commented Dec 8, 2016

            // for error recovery, we parse `int (x, y) = e` as a deconstruction.

More specifically, this is now the name of a kind of assignment expression in which the left-hand-side is of one of a couple of forms.


In reply to: 265670754 [](ancestors = 265670754,265577755)


Refers to: src/Compilers/CSharp/Portable/Parser/LanguageParser.cs:9220 in 1003470. [](commit_id = 1003470, deletion_comment = True)

@gafter
Copy link
Member Author

gafter commented Dec 8, 2016

All comments have been addressed. May I have a second code review, please?

@@ -670,9 +673,7 @@ protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics)
throw ExceptionUtilities.UnexpectedValue(_deconstruction.Kind());
}

TypeSymbol result = this._type;
Debug.Assert((object)result != null);
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert((object)result != null); [](start = 16, length = 37)

Please restore the original assert. The earlier we detect the problem the better. #Closed

{
static void Main(string[] args)
{
foreach (M(out var x1) in new[] { 1, 2, 3 })
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foreach (M(out var x1) in new[] { 1, 2, 3 }) [](start = 8, length = 44)

Please add similar test for a var pattern variable. #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the very next test here.


In reply to: 91582314 [](ancestors = 91582314)

@@ -2570,7 +2570,7 @@ class C
expectedIndentation: 8);
}

[WpfFact, Trait(Traits.Feature, Traits.Features.SmartIndent)]
[WpfFact(Skip = "test is no longer correct due to language changes"), Trait(Traits.Feature, Traits.Features.SmartIndent)]
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WpfFact(Skip = "test is no longer correct due to language changes") [](start = 8, length = 68)

Do we have an issue tracking this? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking what? Language changes? The deletion of this test?


In reply to: 91584597 [](ancestors = 91584597)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the test is skipped, a follow-up should follow. It can take many forms: remove, adjust, test new scenarios enabled by the change. Deciding what to do will be a part of resolving that issue, unless we can do that in context of this change.


In reply to: 91585635 [](ancestors = 91585635,91584597)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not be disabling tests in this manner. If a test is disabled it needs a tracking issue. If it's no longer valid then it should be deleted and we should make the appropriate team (IDE in this case) aware of the change.

Debug.Assert((object)this._type != null);
if ((object)this._type == null)
{
SetType(_nodeBinder.CreateErrorType("var"));
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetType(_nodeBinder.CreateErrorType("var")); [](start = 20, length = 44)

There isn't a mechanism to "enforce" that we only get here for broken code, because we don't have the necessary compilation diagnostics in hand. Ideas for doing that are welcome.

I believe we should never get here for an Out Var or a pattern variable. It should be fairly easy to detect such cases by examining the syntax. Please add the detection and a throw for them. #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 8, 2016

I am done with review pass. The remaining issues that I think should be addressed are:

@AlekseyTs
Copy link
Contributor

LGTM

@jcouv
Copy link
Member

jcouv commented Dec 12, 2016

@dotnet-bot test linux_debug_prtest please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants