-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Rebase target-typed-new branch on top of master #39658
Changes from 23 commits
e3346a7
c91b8a9
6e801fe
1e02465
e565cf2
99f386d
a731674
f4ca425
73c9824
4a362d2
fc55e5e
8e6d3e0
ccee382
067bf52
fac36f2
c12c38c
d660443
b421d5c
8fbe7fa
965c50c
c415912
cff10ef
471dfce
1522da4
a1fc531
14a7dc9
e6a448f
85ec0a7
456666b
0ae0f80
aa48a61
e19c690
b97cac0
c082eff
e49bc5e
9492d27
78613a4
5461453
0823903
49887e7
c6aba72
c0e7d27
a551583
b9e2751
6a10fce
4961ff1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -209,6 +209,7 @@ a[e] | |
x++ | ||
x-- | ||
new X() | ||
new() | ||
typeof(T) | ||
default(T) | ||
default | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,6 +125,11 @@ protected BoundExpression CreateConversion( | |
} | ||
} | ||
|
||
if (conversion.IsObjectCreation) | ||
{ | ||
return ConvertObjectCreationExpression((BoundUnconvertedObjectCreationExpression)source, isCast, destination, diagnostics); | ||
} | ||
|
||
if (conversion.IsUserDefined) | ||
{ | ||
// User-defined conversions are likely to be represented as multiple | ||
|
@@ -152,6 +157,64 @@ protected BoundExpression CreateConversion( | |
{ WasCompilerGenerated = wasCompilerGenerated }; | ||
} | ||
|
||
private BoundExpression ConvertObjectCreationExpression(BoundUnconvertedObjectCreationExpression node, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics) | ||
{ | ||
var arguments = AnalyzedArguments.GetInstance(node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt); | ||
BoundExpression expr = BindObjectCreationExpression(node, destination.StrippedType(), arguments, diagnostics); | ||
if (destination.IsNullableType()) | ||
{ | ||
// We manually create an ImplicitNullable conversion | ||
// if the destination is nullable, in which case we | ||
// target the underlying type e.g. `S? x = new();` | ||
// is actually identical to `S? x = new S();`. | ||
var conversion = new Conversion(ConversionKind.ImplicitNullable, Conversion.IdentityUnderlying); | ||
333fred marked this conversation as resolved.
Show resolved
Hide resolved
|
||
expr = new BoundConversion( | ||
node.Syntax, | ||
operand: expr, | ||
conversion: conversion, | ||
@checked: false, | ||
explicitCastInCode: isCast, | ||
conversionGroupOpt: new ConversionGroup(conversion), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't want the ObjectCreation conversion to be picked up by nullable walker, so the the conversion group has to be backwards.. (I noticed there's a plan to remove this #27163) #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what backwards means here? Honestly, we can probably close #27163, I don't think we'll be removing ConversionGroup. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Talking with @cston, I'm going to close that issue. |
||
constantValueOpt: expr.ConstantValue, | ||
type: destination); | ||
} | ||
arguments.Free(); | ||
return expr; | ||
} | ||
|
||
private BoundExpression BindObjectCreationExpression(BoundUnconvertedObjectCreationExpression node, TypeSymbol type, AnalyzedArguments arguments, DiagnosticBag diagnostics) | ||
{ | ||
var syntax = node.Syntax; | ||
switch (type.TypeKind) | ||
{ | ||
case TypeKind.Struct: | ||
case TypeKind.Class when !type.IsAnonymousType: // We don't want to enable object creation with unspeakable types | ||
return BindClassCreationExpression(syntax, type.Name, typeNode: syntax, (NamedTypeSymbol)type, arguments, diagnostics, node.InitializerOpt); | ||
case TypeKind.TypeParameter: | ||
return BindTypeParameterCreationExpression(syntax, (TypeParameterSymbol)type, arguments, node.InitializerOpt, typeSyntax: syntax, diagnostics); | ||
case TypeKind.Delegate: | ||
return BindDelegateCreationExpression(syntax, (NamedTypeSymbol)type, arguments, node.InitializerOpt, diagnostics); | ||
case TypeKind.Array: | ||
case TypeKind.Enum: | ||
case TypeKind.Class: | ||
Error(diagnostics, ErrorCode.ERR_TypelessNewIllegalTargetType, syntax, type); | ||
alrz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
goto case TypeKind.Error; | ||
case TypeKind.Interface: | ||
Error(diagnostics, ErrorCode.ERR_NoNewAbstract, syntax, type); | ||
goto case TypeKind.Error; | ||
case TypeKind.Pointer: | ||
Error(diagnostics, ErrorCode.ERR_UnsafeTypeInObjectCreation, syntax, type); | ||
goto case TypeKind.Error; | ||
case TypeKind.Dynamic: | ||
Error(diagnostics, ErrorCode.ERR_NoConstructors, syntax, type); | ||
goto case TypeKind.Error; | ||
case TypeKind.Error: | ||
return MakeBadExpressionForObjectCreation(syntax, type, arguments, node.InitializerOpt, typeSyntax: syntax, diagnostics); | ||
case var v: | ||
throw ExceptionUtilities.UnexpectedValue(v); | ||
} | ||
} | ||
|
||
#nullable enable | ||
/// <summary> | ||
/// Rewrite the expressions in the switch expression arms to add a conversion to the destination type. | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -587,8 +587,8 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Di | |
{ | ||
// If we found an operator, we'll have given the `default` literal a type. | ||
// Otherwise, we'll have reported the problem in ReportBinaryOperatorError. | ||
resultLeft = BindToNaturalType(resultLeft, diagnostics, reportDefaultMissingType: false); | ||
resultRight = BindToNaturalType(resultRight, diagnostics, reportDefaultMissingType: false); | ||
resultLeft = BindToNaturalType(resultLeft, diagnostics, reportNoTargetType: false); | ||
resultRight = BindToNaturalType(resultRight, diagnostics, reportNoTargetType: false); | ||
} | ||
|
||
hasErrors = hasErrors || resultConstant != null && resultConstant.IsBad; | ||
|
@@ -653,8 +653,8 @@ private bool BindSimpleBinaryOperatorParts(BinaryExpressionSyntax node, Diagnost | |
{ | ||
resultSignature = signature; | ||
HashSet<DiagnosticInfo> useSiteDiagnostics = null; | ||
bool leftDefault = left.IsLiteralDefault(); | ||
bool rightDefault = right.IsLiteralDefault(); | ||
bool leftDefault = left.IsLiteralDefaultOrTypelessNew(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
📝 let's make sure that the speclet captures the rules around |
||
bool rightDefault = right.IsLiteralDefaultOrTypelessNew(); | ||
foundOperator = !isObjectEquality || BuiltInOperators.IsValidObjectEquality(Conversions, leftType, leftNull, leftDefault, rightType, rightNull, rightDefault, ref useSiteDiagnostics); | ||
diagnostics.Add(node, useSiteDiagnostics); | ||
} | ||
|
@@ -699,18 +699,20 @@ private static void ReportBinaryOperatorError(ExpressionSyntax node, DiagnosticB | |
{ | ||
bool leftDefault = left.IsLiteralDefault(); | ||
bool rightDefault = right.IsLiteralDefault(); | ||
if ((operatorToken.Kind() == SyntaxKind.EqualsEqualsToken || operatorToken.Kind() == SyntaxKind.ExclamationEqualsToken)) | ||
if (operatorToken.Kind() != SyntaxKind.EqualsEqualsToken && operatorToken.Kind() != SyntaxKind.ExclamationEqualsToken) | ||
{ | ||
if (leftDefault && rightDefault) | ||
if (leftDefault || rightDefault) | ||
{ | ||
Error(diagnostics, ErrorCode.ERR_AmbigBinaryOpsOnDefault, node, operatorToken.Text); | ||
// other than == and !=, binary operators are disallowed on `default` literal | ||
Error(diagnostics, ErrorCode.ERR_BadOpOnNullOrDefaultOrNew, node, operatorToken.Text, "default"); | ||
return; | ||
} | ||
} | ||
else if (leftDefault || rightDefault) | ||
|
||
if ((leftDefault || left.IsTypelessNew()) && | ||
(rightDefault || right.IsTypelessNew())) | ||
{ | ||
// other than == and !=, binary operators are disallowed on `default` literal | ||
Error(diagnostics, ErrorCode.ERR_BadOpOnNullOrDefault, node, operatorToken.Text, "default"); | ||
Error(diagnostics, ErrorCode.ERR_AmbigBinaryOpsOnDefaultOrNew, node, operatorToken.Text, left.Display, right.Display); | ||
return; | ||
} | ||
|
||
|
@@ -1113,7 +1115,7 @@ private TypeSymbol GetBinaryOperatorErrorType(BinaryOperatorKind kind, Diagnosti | |
|
||
private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution(BinaryOperatorKind kind, BoundExpression left, BoundExpression right, CSharpSyntaxNode node, DiagnosticBag diagnostics, out LookupResultKind resultKind, out ImmutableArray<MethodSymbol> originalUserDefinedOperators) | ||
{ | ||
if (!IsDefaultLiteralAllowedInBinaryOperator(kind, left, right)) | ||
if (!IsTypelessExpressionAllowedInBinaryOperator(kind, left, right)) | ||
{ | ||
resultKind = LookupResultKind.OverloadResolutionFailure; | ||
originalUserDefinedOperators = default(ImmutableArray<MethodSymbol>); | ||
|
@@ -1182,8 +1184,21 @@ private void ReportObsoleteAndFeatureAvailabilityDiagnostics(MethodSymbol operat | |
} | ||
} | ||
|
||
private bool IsDefaultLiteralAllowedInBinaryOperator(BinaryOperatorKind kind, BoundExpression left, BoundExpression right) | ||
private bool IsTypelessExpressionAllowedInBinaryOperator(BinaryOperatorKind kind, BoundExpression left, BoundExpression right) | ||
{ | ||
// The default literal is only allowed with equality operators and both operands cannot be typeless at the same time. | ||
// Note: we only need to restrict expressions that can be converted to *any* type, in which case the resolution could always succeed. | ||
|
||
if (left.IsTypelessNew()) | ||
{ | ||
return !right.IsLiteralDefaultOrTypelessNew(); | ||
alrz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if (right.IsTypelessNew()) | ||
{ | ||
return !left.IsLiteralDefault(); | ||
} | ||
|
||
bool isEquality = kind == BinaryOperatorKind.Equal || kind == BinaryOperatorKind.NotEqual; | ||
if (isEquality) | ||
{ | ||
|
@@ -2313,7 +2328,7 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper | |
{ | ||
// Dev10 does not allow unary prefix operators to be applied to the null literal | ||
// (or other typeless expressions). | ||
Error(diagnostics, ErrorCode.ERR_BadOpOnNullOrDefault, node, operatorText, operand.Display); | ||
Error(diagnostics, ErrorCode.ERR_BadOpOnNullOrDefaultOrNew, node, operatorText, operand.Display); | ||
} | ||
|
||
// If the operand is bad, avoid generating cascading errors. | ||
|
@@ -3420,7 +3435,7 @@ private BoundExpression BindNullCoalescingOperator(BinaryExpressionSyntax node, | |
// The specification does not permit the left hand side to be a default literal | ||
if (leftOperand.IsLiteralDefault()) | ||
{ | ||
Error(diagnostics, ErrorCode.ERR_BadOpOnNullOrDefault, node, node.OperatorToken.Text, "default"); | ||
Error(diagnostics, ErrorCode.ERR_BadOpOnNullOrDefaultOrNew, node, node.OperatorToken.Text, "default"); | ||
|
||
return new BoundNullCoalescingOperator(node, leftOperand, rightOperand, | ||
Conversion.NoConversion, BoundNullCoalescingOperatorResultKind.NoCommonType, CreateErrorType(), hasErrors: true); | ||
|
@@ -3862,6 +3877,8 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node | |
hasErrors = constantValue != null && constantValue.IsBad; | ||
} | ||
|
||
trueExpr = BindToNaturalType(trueExpr, diagnostics, reportNoTargetType: false); | ||
falseExpr = BindToNaturalType(falseExpr, diagnostics, reportNoTargetType: false); | ||
return new BoundConditionalOperator(node, isRef, condition, trueExpr, falseExpr, constantValue, type, hasErrors); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 let's remember to capture that in speclet #Closed