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

Handle nested nullability in assignments and local declarations #26356

Merged
merged 38 commits into from
May 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a950167
Handle nested nullability in assignments and local declarations
cston Apr 12, 2018
e5501f7
Misc.
cston Apr 25, 2018
c843ba3
Merge remote-tracking branch 'upstream/features/NullableReferenceTypes'
cston Apr 26, 2018
1b57e55
Merge remote-tracking branch 'upstream/features/NullableReferenceType…
cston May 1, 2018
b1849df
Add ConversionGroup
cston May 2, 2018
5f6c53f
Merge remote-tracking branch 'upstream/features/NullableReferenceTypes'
cston May 10, 2018
5e00cd1
Misc.
cston May 10, 2018
33b775f
Change ApplyConversion targetType to TypeSymbol
cston May 16, 2018
50d0670
Merge remote-tracking branch 'upstream/features/NullableReferenceType…
cston May 16, 2018
a283798
Fix build
cston May 16, 2018
b0f89cb
Remove RemoveImplicitConversion
cston May 16, 2018
5539fb9
Fix tests
cston May 16, 2018
5feb62b
Add tests
cston May 16, 2018
552e663
Fix build
cston May 16, 2018
2929f12
More foreach
cston May 16, 2018
a58713a
Fix tests
cston May 17, 2018
13e2977
Fix tests
cston May 17, 2018
eb7e89b
Fix tests
cston May 17, 2018
e479624
PR feedback
cston May 17, 2018
12f36c6
PR feedback
cston May 17, 2018
dc87008
More PR feedback
cston May 17, 2018
35eafd5
Update comments
cston May 17, 2018
353c03d
Fix build
cston May 18, 2018
4c92076
Update comments
cston May 18, 2018
d6962a1
PR feedback
cston May 18, 2018
0c9d598
Handle top-level nullability in HasImplicitReferenceConversion
cston May 18, 2018
a331307
PR feedback
cston May 18, 2018
481a989
PR feedback
cston May 18, 2018
3eaa05e
PR feedback
cston May 21, 2018
c9ee003
Enable test
cston May 21, 2018
9d046d5
PR feedback
cston May 23, 2018
bccac62
Misc.
cston May 24, 2018
5f8c4bb
Enable tests
cston May 24, 2018
dab385a
PR feedback
cston May 24, 2018
8de66cc
PR feedback
cston May 24, 2018
cff6353
PR feedback
cston May 24, 2018
55bfbf0
PR feedback
cston May 25, 2018
9e72ca8
PR feedback
cston May 25, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 34 additions & 36 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(
return new BoundDeconstructionAssignmentOperator(
node,
DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: true),
new BoundConversion(boundRHS.Syntax, boundRHS, Conversion.Deconstruction, @checked: false, explicitCastInCode: false, isExplicitlyNullable: false,
new BoundConversion(boundRHS.Syntax, boundRHS, Conversion.Deconstruction, @checked: false, explicitCastInCode: false, conversionGroupOpt: null,
constantValueOpt: null, type: type, hasErrors: true),
resultIsUsed,
voidType,
Expand Down Expand Up @@ -156,7 +156,7 @@ private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(
conversion,
@checked: false,
explicitCastInCode: false,
isExplicitlyNullable: false,
conversionGroupOpt: null,
constantValueOpt: null,
type: returnType,
hasErrors: hasErrors)
Expand Down
44 changes: 23 additions & 21 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1883,18 +1883,20 @@ private BoundExpression BindCast(CastExpressionSyntax node, DiagnosticBag diagno
!operand.Type.IsNullableType() &&
targetType.GetNullableUnderlyingType() != operand.Type)
{
return BindExplicitNullableCastFromNonNullable(node, operand, targetType, diagnostics);
return BindExplicitNullableCastFromNonNullable(node, operand, targetTypeWithNullability, diagnostics);
}

return BindCastCore(node, operand, targetType, wasCompilerGenerated: operand.WasCompilerGenerated, isExplicitlyNullable: targetTypeWithNullability.IsNullable.GetValueOrDefault(), diagnostics: diagnostics);
return BindCastCore(node, operand, targetTypeWithNullability, wasCompilerGenerated: operand.WasCompilerGenerated, diagnostics: diagnostics);
}

private BoundExpression BindCastCore(ExpressionSyntax node, BoundExpression operand, TypeSymbol targetType, bool wasCompilerGenerated, bool isExplicitlyNullable, DiagnosticBag diagnostics)
private BoundExpression BindCastCore(ExpressionSyntax node, BoundExpression operand, TypeSymbolWithAnnotations targetTypeWithNullability, bool wasCompilerGenerated, DiagnosticBag diagnostics)
{
Debug.Assert(!targetType.IsNullableType() || isExplicitlyNullable);
TypeSymbol targetType = targetTypeWithNullability.TypeSymbol;
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
Conversion conversion = this.Conversions.ClassifyConversionFromExpression(operand, targetType, ref useSiteDiagnostics, forCast: true);
diagnostics.Add(node, useSiteDiagnostics);

var conversionGroup = new ConversionGroup(conversion, targetTypeWithNullability);
if (operand.HasAnyErrors || targetType.IsErrorType() || !conversion.IsValid || targetType.IsStatic)
{
GenerateExplicitConversionErrors(diagnostics, node, conversion, operand, targetType);
Expand All @@ -1905,13 +1907,13 @@ private BoundExpression BindCastCore(ExpressionSyntax node, BoundExpression oper
conversion,
@checked: CheckOverflowAtRuntime,
explicitCastInCode: true,
isExplicitlyNullable: isExplicitlyNullable,
conversionGroup,
constantValueOpt: ConstantValue.NotAvailable,
type: targetType,
hasErrors: true);
}

return CreateConversion(node, operand, conversion, isCast: true, isExplicitlyNullable: isExplicitlyNullable, wasCompilerGenerated: wasCompilerGenerated, destination: targetType, diagnostics: diagnostics);
return CreateConversion(node, operand, conversion, isCast: true, conversionGroup, wasCompilerGenerated: wasCompilerGenerated, destination: targetType, diagnostics: diagnostics);
}

private void GenerateExplicitConversionErrors(
Expand Down Expand Up @@ -2052,38 +2054,38 @@ private void GenerateExplicitConversionErrorsForTupleLiteralArguments(
/// This particular check is done in the binder because it involves conversion processing rules (like overflow
/// checking and constant folding) which are not handled by Conversions.
/// </summary>
private BoundExpression BindExplicitNullableCastFromNonNullable(ExpressionSyntax node, BoundExpression operand, TypeSymbol targetType, DiagnosticBag diagnostics)
private BoundExpression BindExplicitNullableCastFromNonNullable(ExpressionSyntax node, BoundExpression operand, TypeSymbolWithAnnotations targetType, DiagnosticBag diagnostics)
{
Debug.Assert(targetType != null && targetType.IsNullableType());
Debug.Assert((object)targetType != null && targetType.IsNullableType());
Debug.Assert(operand.Type != null && !operand.Type.IsNullableType());

// Section 6.2.3 of the spec only applies when the non-null version of the types involved have a
// built in conversion.
HashSet<DiagnosticInfo> unused = null;
var underlyingTargetType = targetType.GetNullableUnderlyingType();
var underlyingConversion = Conversions.ClassifyBuiltInConversion(operand.Type, underlyingTargetType, ref unused);
TypeSymbolWithAnnotations underlyingTargetType = targetType.GetNullableUnderlyingType();
var underlyingConversion = Conversions.ClassifyBuiltInConversion(operand.Type, underlyingTargetType.TypeSymbol, ref unused);
if (!underlyingConversion.Exists)
{
return BindCastCore(node, operand, targetType, wasCompilerGenerated: operand.WasCompilerGenerated, isExplicitlyNullable: true, diagnostics: diagnostics);
return BindCastCore(node, operand, targetType, wasCompilerGenerated: operand.WasCompilerGenerated, diagnostics: diagnostics);
}

var bag = DiagnosticBag.GetInstance();
try
{
var underlyingExpr = BindCastCore(node, operand, targetType.GetNullableUnderlyingType(), wasCompilerGenerated: false, isExplicitlyNullable: false, diagnostics: bag);
var underlyingExpr = BindCastCore(node, operand, underlyingTargetType, wasCompilerGenerated: false, diagnostics: bag);
if (underlyingExpr.HasErrors || bag.HasAnyErrors())
{
Error(diagnostics, ErrorCode.ERR_NoExplicitConv, node, operand.Type, targetType);
Error(diagnostics, ErrorCode.ERR_NoExplicitConv, node, operand.Type, targetType.TypeSymbol);

return new BoundConversion(
node,
operand,
Conversion.NoConversion,
@checked: CheckOverflowAtRuntime,
explicitCastInCode: true,
isExplicitlyNullable: true,
conversionGroupOpt: new ConversionGroup(Conversion.NoConversion, explicitType: targetType),
constantValueOpt: ConstantValue.NotAvailable,
type: targetType,
type: targetType.TypeSymbol,
hasErrors: true);
}

Expand All @@ -2093,10 +2095,10 @@ private BoundExpression BindExplicitNullableCastFromNonNullable(ExpressionSyntax
if (underlyingExpr.ConstantValue != null)
{
underlyingExpr.WasCompilerGenerated = true;
return BindCastCore(node, underlyingExpr, targetType, wasCompilerGenerated: operand.WasCompilerGenerated, isExplicitlyNullable: true, diagnostics: diagnostics);
return BindCastCore(node, underlyingExpr, targetType, wasCompilerGenerated: operand.WasCompilerGenerated, diagnostics: diagnostics);
}

return BindCastCore(node, operand, targetType, wasCompilerGenerated: operand.WasCompilerGenerated, isExplicitlyNullable: true, diagnostics: diagnostics);
return BindCastCore(node, operand, targetType, wasCompilerGenerated: operand.WasCompilerGenerated, diagnostics: diagnostics);
}
finally
{
Expand Down Expand Up @@ -2540,7 +2542,7 @@ private void CoerceArguments<TMember>(
//CONSIDER: Return a bad expression so that HasErrors is true?
}

arguments[arg] = CreateConversion(argument.Syntax, argument, kind, isCast: false, isExplicitlyNullable: false, type, diagnostics);
arguments[arg] = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, type, diagnostics);
}
else if (argument.Kind == BoundKind.OutVariablePendingInference)
{
Expand Down Expand Up @@ -6230,7 +6232,7 @@ protected BoundExpression BindFieldAccess(
Conversion.ImplicitNumeric,
@checked: true,
explicitCastInCode: false,
isExplicitlyNullable: false,
conversionGroupOpt: null,
constantValueOpt: expr.ConstantValue,
type: underlyingType);
}
Expand Down Expand Up @@ -6671,7 +6673,7 @@ private BoundExpression ConvertToArrayIndex(BoundExpression index, SyntaxNode no
GenerateImplicitConversionError(diagnostics, node, failedConversion, index, int32);

// Suppress any additional diagnostics
return CreateConversion(index.Syntax, index, failedConversion, isCast: false, isExplicitlyNullable: false, destination: int32, diagnostics: new DiagnosticBag());
return CreateConversion(index.Syntax, index, failedConversion, isCast: false, conversionGroupOpt: null, destination: int32, diagnostics: new DiagnosticBag());
}

return result;
Expand Down Expand Up @@ -6700,7 +6702,7 @@ private BoundExpression TryImplicitConversionToArrayIndex(BoundExpression expr,
conversion = conversion.SetArrayIndexConversionForDynamic();
}

BoundExpression result = CreateConversion(expr.Syntax, expr, conversion, isCast: false, isExplicitlyNullable: false, destination: type, diagnostics: attemptDiagnostics); // UNDONE: was cast?
BoundExpression result = CreateConversion(expr.Syntax, expr, conversion, isCast: false, conversionGroupOpt: null, destination: type, diagnostics: attemptDiagnostics); // UNDONE: was cast?
Debug.Assert(result != null); // If this ever fails (it shouldn't), then put a null-check around the diagnostics update.

diagnostics.AddRange(attemptDiagnostics);
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2331,7 +2331,7 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper

var signature = best.Signature;

var resultOperand = CreateConversion(operand.Syntax, operand, best.Conversion, isCast: false, isExplicitlyNullable: false, signature.OperandType, diagnostics);
var resultOperand = CreateConversion(operand.Syntax, operand, best.Conversion, isCast: false, conversionGroupOpt: null, signature.OperandType, diagnostics);
var resultType = signature.ReturnType;
UnaryOperatorKind resultOperatorKind = signature.Kind;
var resultMethod = signature.Method;
Expand Down
12 changes: 6 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1686,7 +1686,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType,
diagnostics = new DiagnosticBag();
}

return CreateConversion(expression.Syntax, expression, conversion, isCast: false, isExplicitlyNullable: false, targetType, diagnostics);
return CreateConversion(expression.Syntax, expression, conversion, isCast: false, conversionGroupOpt: null, targetType, diagnostics);
}

internal void GenerateAnonymousFunctionConversionError(DiagnosticBag diagnostics, SyntaxNode syntax,
Expand Down Expand Up @@ -2157,7 +2157,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, Diagnostic
// The expression could not be bound. Insert a fake conversion
// around it to bool and keep on going.
// NOTE: no user-defined conversion candidates.
return BoundConversion.Synthesized(node, expr, Conversion.NoConversion, false, explicitCastInCode: false, isExplicitlyNullable: false, ConstantValue.NotAvailable, boolean, hasErrors: true);
return BoundConversion.Synthesized(node, expr, Conversion.NoConversion, false, explicitCastInCode: false, conversionGroupOpt: null, ConstantValue.NotAvailable, boolean, hasErrors: true);
}

// Oddly enough, "if(dyn)" is bound not as a dynamic conversion to bool, but as a dynamic
Expand Down Expand Up @@ -2210,7 +2210,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, Diagnostic
source: expr,
conversion: conversion,
isCast: false,
isExplicitlyNullable: false,
conversionGroupOpt: null,
wasCompilerGenerated: true,
destination: boolean,
diagnostics: diagnostics);
Expand All @@ -2228,7 +2228,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, Diagnostic
Debug.Assert(resultKind == LookupResultKind.Empty, "How could overload resolution fail if a user-defined true operator was found?");
Debug.Assert(originalUserDefinedOperators.IsEmpty, "How could overload resolution fail if a user-defined true operator was found?");
GenerateImplicitConversionError(diagnostics, node, conversion, expr, boolean);
return BoundConversion.Synthesized(node, expr, Conversion.NoConversion, false, explicitCastInCode: false, isExplicitlyNullable: false, ConstantValue.NotAvailable, boolean, hasErrors: true);
return BoundConversion.Synthesized(node, expr, Conversion.NoConversion, false, explicitCastInCode: false, conversionGroupOpt: null, ConstantValue.NotAvailable, boolean, hasErrors: true);
}

UnaryOperatorSignature signature = best.Signature;
Expand All @@ -2238,7 +2238,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, Diagnostic
expr,
best.Conversion,
isCast: false,
isExplicitlyNullable: false,
conversionGroupOpt: null,
destination: best.Signature.OperandType,
diagnostics: diagnostics);

Expand Down Expand Up @@ -2672,7 +2672,7 @@ internal BoundExpression CreateReturnConversion(
}
}

return CreateConversion(argument.Syntax, argument, conversion, isCast: false, isExplicitlyNullable: false, returnType, diagnostics);
return CreateConversion(argument.Syntax, argument, conversion, isCast: false, conversionGroupOpt: null, returnType, diagnostics);
}

private BoundTryStatement BindTryStatement(TryStatementSyntax node, DiagnosticBag diagnostics)
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
builder.CollectionConversion,
@checked: CheckOverflowAtRuntime,
explicitCastInCode: false,
isExplicitlyNullable: false,
conversionGroupOpt: null,
ConstantValue.NotAvailable,
builder.CollectionType);

Expand Down
Loading