Skip to content

Commit

Permalink
Prototype for "default": semantic model, constant value and more tests (
Browse files Browse the repository at this point in the history
dotnet#16425)

Conflicts:
	src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
	src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
	src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
	src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
  • Loading branch information
jcouv committed Mar 11, 2017
1 parent cd510fd commit 82268d0
Show file tree
Hide file tree
Showing 42 changed files with 561 additions and 97 deletions.
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 @@ -920,6 +920,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 @@ -773,6 +773,11 @@ private BoundExpression BindDeclarationVariables(TypeSymbol declType, VariableDe
}
}

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

private BoundExpression BindTupleExpression(TupleExpressionSyntax node, DiagnosticBag diagnostics)
{
SeparatedSyntaxList<ArgumentSyntax> arguments = node.Arguments;
Expand Down Expand Up @@ -1057,7 +1062,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 @@ -1851,8 +1856,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 @@ -5039,7 +5042,6 @@ private BoundExpression BindMemberAccessWithBoundLeft(
return BadExpression(node, boundLeft);
}

// PROTOTYPE(default) unify with case above
// No member accesses on default
if (boundLeft.IsLiteralDefault())
{
Expand Down Expand Up @@ -5216,7 +5218,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 @@ -409,6 +409,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 @@ -2672,6 +2672,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 @@ -3063,8 +3069,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 @@ -724,6 +724,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.NamespaceExpression)
{
diagnostics.Add(ErrorCode.ERR_BadSKunknown, ultimateReceiver.Syntax.Location, ultimateReceiver.Syntax, MessageID.IDS_SK_NAMESPACE.Localize());
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 @@ -1005,8 +1005,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 @@ -3197,7 +3200,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 @@ -500,13 +500,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 @@ -810,8 +810,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 @@ -973,7 +973,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 @@ -460,7 +460,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 @@ -517,7 +517,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 @@ -939,7 +939,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 @@ -136,7 +136,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 @@ -849,6 +849,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 @@ -2184,7 +2187,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 @@ -5023,4 +5026,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_PatternDynamicType" xml:space="preserve">
<value>It is not legal to use the type 'dynamic' in a pattern.</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 @@ -2637,7 +2637,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 @@ -1426,7 +1426,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
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 @@ -1461,5 +1461,8 @@ internal enum ErrorCode
#endregion more stragglers for C# 7

ERR_Merge_conflict_marker_encountered = 8300,

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 @@ -608,7 +608,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
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static BoundBlock Rewrite(
{
Debug.Assert(submissionResultType.SpecialType != SpecialType.System_Void);

var trailingExpression = new BoundDefaultOperator(method.GetNonNullSyntaxNode(), submissionResultType);
var trailingExpression = new BoundDefaultLiteral(method.GetNonNullSyntaxNode(), submissionResultType);
var newStatements = block.Statements.Add(new BoundReturnStatement(trailingExpression.Syntax, RefKind.None, trailingExpression));
block = new BoundBlock(block.Syntax, ImmutableArray<LocalSymbol>.Empty, newStatements) { WasCompilerGenerated = true };
#if DEBUG
Expand Down
Loading

0 comments on commit 82268d0

Please sign in to comment.