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

Prototype for "default": semantic model, constant value and more tests #16425

Merged
merged 6 commits into from
Feb 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,10 @@ public ConstantValue FoldConstantConversion(
case ConversionKind.NullLiteral:
return sourceConstantValue;

case ConversionKind.DefaultLiteral:
Debug.Assert(sourceConstantValue.IsDefaultLiteral);
return destination.GetDefaultValue();

case ConversionKind.ImplicitConstant:
return FoldConstantNumericConversion(syntax, sourceConstantValue, destination, diagnostics);

Expand Down
14 changes: 8 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic
return BindDefaultExpression((DefaultExpressionSyntax)node, diagnostics);

case SyntaxKind.DefaultLiteral:
return new BoundDefaultOperator((DefaultLiteralSyntax)node);
return BindDefaultLiteral((DefaultLiteralSyntax)node);

case SyntaxKind.TypeOfExpression:
return BindTypeOf((TypeOfExpressionSyntax)node, diagnostics);
Expand Down Expand Up @@ -656,6 +656,11 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic
}
}

private static BoundExpression BindDefaultLiteral(DefaultLiteralSyntax node)
{
return new BoundDefaultLiteral(node, ConstantValue.DefaultLiteral, type: null);
}
Copy link
Member

Choose a reason for hiding this comment

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

Is (T)default a constant? Is const object o = (T)default; legal?

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 will check. I have a test for int x = (short)default; (legal), but not with a generic cast.
My expectation is that (T)default should behave the same as default(T).

Copy link
Contributor

Choose a reason for hiding this comment

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

What about scenarios when it is of type user-defined struct? Is it supposed to have constant value then?


private BoundExpression BindTupleExpression(TupleExpressionSyntax node, DiagnosticBag diagnostics)
{
SeparatedSyntaxList<ArgumentSyntax> arguments = node.Arguments;
Expand Down Expand Up @@ -970,7 +975,7 @@ internal static ConstantValue GetConstantSizeOf(TypeSymbol type)
private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, DiagnosticBag diagnostics)
{
TypeSymbol type = this.BindType(node.Type, diagnostics);
return new BoundDefaultOperator(node, type);
return new BoundDefaultLiteral(node, type);
}

/// <summary>
Expand Down Expand Up @@ -1730,8 +1735,6 @@ private void GenerateExplicitConversionErrors(
return;
}

// PROTOTYPE(default) SHould handle default literal here?

if (conversion.ResultKind == LookupResultKind.OverloadResolutionFailure)
{
Debug.Assert(conversion.IsUserDefined);
Expand Down Expand Up @@ -4834,7 +4837,6 @@ private BoundExpression BindMemberAccessWithBoundLeft(
return BadExpression(node, boundLeft);
}

// PROTOTYPE(default) unify with case above
// No member accesses on default
if (boundLeft.IsLiteralDefault())
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if these two cases (default and unbound lambda) can be combined by testingfor (object)leftType == null and in that case report the diagnostic using boundLeft.Display.

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 addressed your other feedback in latest commit, but I couldn't quite see how to factor as you suggested here.

{
Expand Down Expand Up @@ -5011,7 +5013,7 @@ private BoundExpression BindMemberAccessWithBoundLeft(

private static void WarnOnAccessOfOffDefault(SyntaxNode node, BoundExpression boundLeft, DiagnosticBag diagnostics)
{
if (boundLeft != null && boundLeft.Kind == BoundKind.DefaultOperator && boundLeft.ConstantValue == ConstantValue.Null)
if (boundLeft != null && boundLeft.Kind == BoundKind.DefaultLiteral && boundLeft.ConstantValue == ConstantValue.Null)
{
Error(diagnostics, ErrorCode.WRN_DotOnDefault, node, boundLeft.Type);
}
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ private static bool ReportBadDynamicArguments(
// error CS1978: Cannot use an expression of type '__arglist' as an argument to a dynamically dispatched operation
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArg, arg.Syntax, "__arglist");
}
else if (arg.IsLiteralDefault())
{
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArgDefault, arg.Syntax);
hasErrors = true;
}
else
{
// Lambdas,anonymous methods and method groups are the typeless expressions that
Expand Down
8 changes: 6 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2628,6 +2628,12 @@ private BoundExpression BindIsOperator(BinaryExpressionSyntax node, DiagnosticBa

HashSet<DiagnosticInfo> useSiteDiagnostics = null;

if (operand.ConstantValue == ConstantValue.DefaultLiteral)
{
Error(diagnostics, ErrorCode.ERR_DefaultNotValid, node, targetType);
return new BoundIsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true);
}

if (operand.ConstantValue == ConstantValue.Null ||
operand.Kind == BoundKind.MethodGroup ||
operand.Type.SpecialType == SpecialType.System_Void)
Expand Down Expand Up @@ -3019,8 +3025,6 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBa
return new BoundAsOperator(node, operand, typeExpression, Conversion.NullLiteral, resultType);
}

// PROTOTYPE(default) Something needed here for "as" operator

if (operand.Kind == BoundKind.MethodGroup)
{
Error(diagnostics, ErrorCode.ERR_NoExplicitBuiltinConv, node, MessageID.IDS_MethodGroup.Localize(), targetType);
Expand Down
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,10 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r
{
diagnostics.Add(ErrorCode.ERR_NullNotValid, node.Location);
}
else if (ultimateReceiver.IsLiteralDefault())
{
diagnostics.Add(ErrorCode.ERR_DefaultNotValid, node.Location);
}
else if (ultimateReceiver.Kind == BoundKind.Lambda || ultimateReceiver.Kind == BoundKind.UnboundLambda)
{
// Could not find an implementation of the query pattern for source type '{0}'. '{1}' not found.
Expand Down
7 changes: 5 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1026,8 +1026,11 @@ private bool IsValidFixedVariableInitializer(TypeSymbol declType, SourceLocalSym
initializerOpt = GenerateConversionForAssignment(declType, initializerOpt, diagnostics);
if (!initializerOpt.HasAnyErrors)
{
Debug.Assert(initializerOpt.Kind == BoundKind.Conversion && ((BoundConversion)initializerOpt).Operand.IsLiteralNull(),
Debug.Assert(initializerOpt.Kind == BoundKind.Conversion &&
(((BoundConversion)initializerOpt).Operand.IsLiteralNull() ||
((BoundConversion)initializerOpt).Operand.IsLiteralDefault()),
"All other typeless expressions should have conversion errors");

// CONSIDER: this is a very confusing error message, but it's what Dev10 reports.
Error(diagnostics, ErrorCode.ERR_FixedNotNeeded, initializerSyntax);
}
Expand Down Expand Up @@ -3173,7 +3176,7 @@ private BoundStatement BindReturn(ReturnStatementSyntax syntax, DiagnosticBag di
var interactiveInitializerMethod = this.ContainingMemberOrLambda as SynthesizedInteractiveInitializerMethod;
if (interactiveInitializerMethod != null)
{
arg = new BoundDefaultOperator(interactiveInitializerMethod.GetNonNullSyntaxNode(), interactiveInitializerMethod.ResultType);
arg = new BoundDefaultLiteral(interactiveInitializerMethod.GetNonNullSyntaxNode(), interactiveInitializerMethod.ResultType);
}
}

Expand Down
18 changes: 12 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -388,13 +388,19 @@ private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundE
{
TypeSymbol collectionExprType = collectionExpr.Type;

if (collectionExpr.ConstantValue != null && collectionExpr.ConstantValue.IsNull)
if (collectionExpr.ConstantValue != null)
{
// Spec seems to refer to null literals, but Dev10 reports anything known to be null.
Debug.Assert(collectionExpr.ConstantValue.IsNull); // only constant value with no type
diagnostics.Add(ErrorCode.ERR_NullNotValid, _syntax.Expression.Location);

return false;
if (collectionExpr.ConstantValue.IsNull)
{
// Spec seems to refer to null literals, but Dev10 reports anything known to be null.
diagnostics.Add(ErrorCode.ERR_NullNotValid, _syntax.Expression.Location);
return false;
}
else if (collectionExpr.ConstantValue.IsDefaultLiteral)
{
diagnostics.Add(ErrorCode.ERR_DefaultNotValid, _syntax.Expression.Location);
return false;
}
}

if ((object)collectionExprType == null) // There's no way to enumerate something without a type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -815,8 +815,8 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi
}
break;

case BoundKind.DefaultOperator:
var defaultExpression = (BoundDefaultOperator)sourceExpression;
case BoundKind.DefaultLiteral:
var defaultExpression = (BoundDefaultLiteral)sourceExpression;
if ((object)defaultExpression.Type == null)
{
return Conversion.DefaultLiteral;
Expand Down Expand Up @@ -975,7 +975,7 @@ internal static bool HasImplicitConstantExpressionConversion(BoundExpression sou
{
var constantValue = source.ConstantValue;

if (constantValue == null)
if (constantValue == null || (object)source.Type == null)
{
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ public override Symbol ExpressionSymbol
}
}

internal partial class BoundDefaultOperator
internal partial class BoundDefaultLiteral
{
public override ConstantValue ConstantValue
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static bool IsLiteralNull(this BoundExpression node)

public static bool IsLiteralDefault(this BoundExpression node)
{
return node.Kind == BoundKind.DefaultOperator && (object)node.Type == null;
return node.Kind == BoundKind.DefaultLiteral && (object)node.Type == null;
}

// returns true when expression has no side-effects and produces
Expand All @@ -27,7 +27,7 @@ public static bool IsLiteralDefault(this BoundExpression node)
// after some folding/propagation/algebraic transformations.
public static bool IsDefaultValue(this BoundExpression node)
{
if (node.Kind == BoundKind.DefaultOperator)
if (node.Kind == BoundKind.DefaultLiteral)
{
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@
<Field Name="GetFieldFromHandle" Type="MethodSymbol" Null="allow"/>
</Node>

<Node Name="BoundDefaultOperator" Base="BoundExpression">
<Node Name="BoundDefaultLiteral" Base="BoundExpression">
<!-- Type is null in the case of a default literal, and non-null in the case of a fully-spelled out default operator. -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="allow"/>
<Field Name="ConstantValueOpt" Type="ConstantValue" Null="allow"/>
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/BoundTree/BoundTreeVisitors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public virtual R Visit(BoundNode node, A arg)
return VisitArrayAccess(node as BoundArrayAccess, arg);
case BoundKind.TypeOfOperator:
return VisitTypeOfOperator(node as BoundTypeOfOperator, arg);
case BoundKind.DefaultOperator:
return VisitDefaultOperator(node as BoundDefaultOperator, arg);
case BoundKind.DefaultLiteral:
return VisitDefaultLiteral(node as BoundDefaultLiteral, arg);
case BoundKind.IsOperator:
return VisitIsOperator(node as BoundIsOperator, arg);
case BoundKind.AsOperator:
Expand Down
6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Portable/BoundTree/Constructors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -523,14 +523,14 @@ public static BoundBlock SynthesizedNoLocals(SyntaxNode syntax, params BoundStat
}
}

internal partial class BoundDefaultOperator
internal partial class BoundDefaultLiteral
{
public BoundDefaultOperator(SyntaxNode syntax, TypeSymbol type, bool hasErrors = false)
public BoundDefaultLiteral(SyntaxNode syntax, TypeSymbol type, bool hasErrors = false)
: this(syntax, type.GetDefaultValue(), type, hasErrors)
{
}

public BoundDefaultOperator(SyntaxNode syntax)
public BoundDefaultLiteral(SyntaxNode syntax)
: this(syntax, constantValueOpt: null, type: null, hasErrors: false)
{
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/BoundTree/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,7 @@ public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, T
}
}

internal partial class BoundDefaultOperator : IDefaultValueExpression
internal partial class BoundDefaultLiteral : IDefaultValueExpression
{
protected override OperationKind ExpressionKind => OperationKind.DefaultValueExpression;

Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/BoundTree/Formatting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public override object Display
}
}

internal partial class BoundDefaultOperator
internal partial class BoundDefaultLiteral
{
public override object Display
{
Expand Down
20 changes: 19 additions & 1 deletion src/Compilers/CSharp/Portable/CSharpResources.Designer.cs

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

8 changes: 7 additions & 1 deletion src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,9 @@
<data name="ERR_NullNotValid" xml:space="preserve">
<value>Use of null is not valid in this context</value>
</data>
<data name="ERR_DefaultNotValid" xml:space="preserve">
<value>Use of default is not valid in this context</value>
</data>
<data name="ERR_UseDefViolationThis" xml:space="preserve">
<value>The 'this' object cannot be used before all of its fields are assigned to</value>
</data>
Expand Down Expand Up @@ -2169,7 +2172,7 @@ If such a class is used as a base class and if the deriving class defines a dest
<value>Cannot use local variable '{0}' before it is declared. The declaration of the local variable hides the field '{1}'.</value>
</data>
<data name="ERR_ExpressionTreeContainsBadCoalesce" xml:space="preserve">
<value>An expression tree lambda may not contain a coalescing operator with a null literal left-hand side</value>
<value>An expression tree lambda may not contain a coalescing operator with a null or default literal left-hand side</value>
</data>
<data name="ERR_IdentifierExpected" xml:space="preserve">
<value>Identifier expected</value>
Expand Down Expand Up @@ -4956,4 +4959,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_ExpressionVariableInConstructorOrFieldInitializer" xml:space="preserve">
<value>Out variable or pattern variable declarations are not allowed within constructor/field/auto-implemented property initializers.</value>
</data>
<data name="ERR_BadDynamicMethodArgDefault" xml:space="preserve">
<value>Cannot use a type-inferred default operator as an argument to a dynamically dispatched operation.</value>
</data>
</root>
6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ private void EmitExpressionCore(BoundExpression expression, bool used)
EmitAsExpression((BoundAsOperator)expression, used);
break;

case BoundKind.DefaultOperator:
EmitDefaultExpression((BoundDefaultOperator)expression, used);
case BoundKind.DefaultLiteral:
EmitDefaultExpression((BoundDefaultLiteral)expression, used);
break;

case BoundKind.TypeOfOperator:
Expand Down Expand Up @@ -2617,7 +2617,7 @@ private void EmitDefaultValue(TypeSymbol type, bool used, SyntaxNode syntaxNode)
}
}

private void EmitDefaultExpression(BoundDefaultOperator expression, bool used)
private void EmitDefaultExpression(BoundDefaultLiteral expression, bool used)
{
Debug.Assert(expression.Type.SpecialType == SpecialType.System_Decimal ||
expression.Type.GetDefaultValue() == null, "constant should be set on this expression");
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1405,7 +1405,7 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
if (node.OperatorKind.IsChecked() && node.OperatorKind.Operator() == UnaryOperatorKind.UnaryMinus)
{
var origStack = StackDepth();
PushEvalStack(new BoundDefaultOperator(node.Syntax, node.Operand.Type), ExprContext.Value);
PushEvalStack(new BoundDefaultLiteral(node.Syntax, node.Operand.Type), ExprContext.Value);
BoundExpression operand = (BoundExpression)this.Visit(node.Operand);
return node.Update(node.OperatorKind, operand, node.ConstantValueOpt, node.MethodOpt, node.ResultKind, node.Type);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1173,7 +1173,7 @@ private static BoundStatement ChainImplicitStructConstructor(MethodSymbol method
new BoundAssignmentOperator(
syntax,
new BoundThisReference(syntax, containingType),
new BoundDefaultOperator(syntax, containingType),
new BoundDefaultLiteral(syntax, containingType),
RefKind.None,
containingType));
}
Expand Down
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1435,5 +1435,8 @@ internal enum ErrorCode
ERR_OutVarDeconstructionIsNotSupported = 8199,
ERR_ExpressionVariableInConstructorOrFieldInitializer = 8200,
#endregion diagnostics for out var

ERR_BadDynamicMethodArgDefault = 9000,
ERR_DefaultNotValid = 9001,
}
}
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ internal static bool WriteConsideredUse(TypeSymbol type, BoundExpression value)
}
return WriteConsideredUse(null, boundConversion.Operand);
}
case BoundKind.DefaultOperator:
case BoundKind.DefaultLiteral:
return false;
case BoundKind.ObjectCreationExpression:
var init = (BoundObjectCreationExpression)value;
Expand Down
Loading