Skip to content

Commit

Permalink
Pulling "default" literal feature into master branch
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored Apr 15, 2017
2 parents 323c0d3 + dbbf7fc commit d0d07e5
Show file tree
Hide file tree
Showing 65 changed files with 2,687 additions and 146 deletions.
22 changes: 20 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
using System.Collections.Immutable;
using System;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.CodeAnalysis.CSharp
Expand Down Expand Up @@ -104,6 +105,11 @@ protected BoundExpression CreateConversion(
}

ConstantValue constantValue = this.FoldConstantConversion(syntax, source, conversion, destination, diagnostics);
if (conversion.Kind == ConversionKind.DefaultOrNullLiteral && source.Kind == BoundKind.DefaultExpression)
{
source = ((BoundDefaultExpression)source).Update(constantValue, destination);
}

return new BoundConversion(
syntax,
source,
Expand Down Expand Up @@ -896,7 +902,18 @@ public ConstantValue FoldConstantConversion(
}

var sourceConstantValue = source.ConstantValue;
if (sourceConstantValue == null || sourceConstantValue.IsBad)
if (sourceConstantValue == null)
{
if (conversion.Kind == ConversionKind.DefaultOrNullLiteral && source.Kind == BoundKind.DefaultExpression)
{
return destination.GetDefaultValue();
}
else
{
return sourceConstantValue;
}
}
else if (sourceConstantValue.IsBad)
{
return sourceConstantValue;
}
Expand All @@ -917,7 +934,8 @@ public ConstantValue FoldConstantConversion(
return sourceConstantValue;
}

case ConversionKind.NullLiteral:
case ConversionKind.DefaultOrNullLiteral:
// 'default' case is handled above, 'null' is handled here
return sourceConstantValue;

case ConversionKind.ImplicitConstant:
Expand Down
24 changes: 19 additions & 5 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,9 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic
case SyntaxKind.NullLiteralExpression:
return BindLiteralConstant((LiteralExpressionSyntax)node, diagnostics);

case SyntaxKind.DefaultLiteralExpression:
return BindDefaultLiteral(node);

case SyntaxKind.ParenthesizedExpression:
// Parenthesis tokens are ignored, and operand is bound in the context of parent
// expression.
Expand Down Expand Up @@ -636,6 +639,11 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic
}
}

private static BoundExpression BindDefaultLiteral(ExpressionSyntax node)
{
return new BoundDefaultExpression(node, constantValueOpt: null, type: null);
}

private BoundExpression BindRefExpression(ExpressionSyntax node, DiagnosticBag diagnostics)
{
var firstToken = node.GetFirstToken();
Expand Down Expand Up @@ -1049,7 +1057,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 BoundDefaultExpression(node, type);
}

/// <summary>
Expand Down Expand Up @@ -5063,7 +5071,14 @@ private BoundExpression BindMemberAccessWithBoundLeft(
// No member accesses on void
if ((object)leftType != null && leftType.SpecialType == SpecialType.System_Void)
{
DiagnosticInfo diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_BadUnaryOp, SyntaxFacts.GetText(operatorToken.Kind()), leftType);
diagnostics.Add(ErrorCode.ERR_BadUnaryOp, operatorToken.GetLocation(), SyntaxFacts.GetText(operatorToken.Kind()), leftType);
return BadExpression(node, boundLeft);
}

// No member accesses on default
if (boundLeft.IsLiteralDefault())
{
DiagnosticInfo diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_BadUnaryOp, SyntaxFacts.GetText(operatorToken.Kind()), "default");
diagnostics.Add(new CSDiagnostic(diagnosticInfo, operatorToken.GetLocation()));
return BadExpression(node, boundLeft);
}
Expand All @@ -5073,8 +5088,7 @@ private BoundExpression BindMemberAccessWithBoundLeft(
Debug.Assert((object)leftType == null);

var msgId = ((UnboundLambda)boundLeft).MessageID;
DiagnosticInfo diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_BadUnaryOp, SyntaxFacts.GetText(operatorToken.Kind()), msgId.Localize());
diagnostics.Add(new CSDiagnostic(diagnosticInfo, node.Location));
diagnostics.Add(ErrorCode.ERR_BadUnaryOp, node.Location, SyntaxFacts.GetText(operatorToken.Kind()), msgId.Localize());
return BadExpression(node, boundLeft);
}

Expand Down Expand Up @@ -5237,7 +5251,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.DefaultExpression && 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_BadDynamicMethodArgDefaultLiteral, arg.Syntax);
hasErrors = true;
}
else
{
// Lambdas,anonymous methods and method groups are the typeless expressions that
Expand Down
16 changes: 13 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2180,7 +2180,7 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper
{
UnaryOperatorKind kind = SyntaxKindToUnaryOperatorKind(node.Kind());

bool isOperandTypeNull = (object)operand.Type == null;
bool isOperandTypeNull = operand.IsLiteralNull();
if (isOperandTypeNull)
{
// Dev10 does not allow unary prefix operators to be applied to the null literal
Expand Down Expand Up @@ -2974,7 +2974,7 @@ internal static ConstantValue GetIsOperatorConstantResult(TypeSymbol operandType
case ConversionKind.IntegerToPointer:
case ConversionKind.NullToPointer:
case ConversionKind.AnonymousFunction:
case ConversionKind.NullLiteral:
case ConversionKind.DefaultOrNullLiteral:
case ConversionKind.MethodGroup:
// We've either replaced Dynamic with Object, or already bailed out with an error.
throw ExceptionUtilities.UnexpectedValue(conversionKind);
Expand Down Expand Up @@ -3060,7 +3060,17 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBa
// We do not want to warn for the case "null as TYPE" where the null
// is a literal, because the user might be saying it to cause overload resolution
// to pick a particular method
return new BoundAsOperator(node, operand, typeExpression, Conversion.NullLiteral, resultType);
return new BoundAsOperator(node, operand, typeExpression, Conversion.DefaultOrNullLiteral, resultType);
}

if (operand.IsLiteralDefault())
{
var defaultLiteral = (BoundDefaultExpression)operand;
Debug.Assert((object)defaultLiteral.Type == null);
Debug.Assert((object)defaultLiteral.ConstantValueOpt == null);

operand = new BoundDefaultExpression(defaultLiteral.Syntax, constantValueOpt: ConstantValue.Null,
type: GetSpecialType(SpecialType.System_Object, diagnostics, node));
}

if (operand.Kind == BoundKind.MethodGroup)
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,11 @@ private bool CheckValidPatternType(
case ConversionKind.Identity:
case ConversionKind.ImplicitReference:
case ConversionKind.Unboxing:
case ConversionKind.NullLiteral:
case ConversionKind.ImplicitNullable:
// these are the conversions allowed by a pattern match
break;
case ConversionKind.DefaultOrNullLiteral:
throw ExceptionUtilities.UnexpectedValue(conversion.Kind);
//case ConversionKind.ExplicitNumeric: // we do not perform numeric conversions of the operand
//case ConversionKind.ImplicitConstant:
//case ConversionKind.ImplicitNumeric:
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_DefaultLiteralNotValid, 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
25 changes: 14 additions & 11 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.Kind == BoundKind.DefaultExpression),
"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 @@ -2704,10 +2707,10 @@ protected static void GenerateImplicitConversionError(DiagnosticBag diagnostics,
}

protected void GenerateImplicitConversionError(
DiagnosticBag diagnostics,
DiagnosticBag diagnostics,
SyntaxNode syntax,
Conversion conversion,
BoundExpression operand,
Conversion conversion,
BoundExpression operand,
TypeSymbol targetType)
{
Debug.Assert(operand != null);
Expand Down Expand Up @@ -2736,7 +2739,7 @@ protected void GenerateImplicitConversionError(

// If target is a tuple or compatible type with the same number of elements,
// report errors for tuple arguments that failed to convert, which would be more useful.
if (targetType.TryGetElementTypesIfTupleOrCompatible(out targetElementTypes) &&
if (targetType.TryGetElementTypesIfTupleOrCompatible(out targetElementTypes) &&
targetElementTypes.Length == tuple.Arguments.Length)
{
GenerateImplicitConversionErrorsForTupleLiteralArguments(diagnostics, tuple.Arguments, targetElementTypes);
Expand All @@ -2759,7 +2762,7 @@ protected void GenerateImplicitConversionError(
GenerateImplicitConversionError(diagnostics, this.Compilation, syntax, conversion, sourceType, targetType, operand.ConstantValue);
return;
}

if (operand.IsLiteralNull())
{
if (targetType.TypeKind == TypeKind.TypeParameter)
Expand Down Expand Up @@ -2809,8 +2812,8 @@ protected void GenerateImplicitConversionError(
}

private void GenerateImplicitConversionErrorsForTupleLiteralArguments(
DiagnosticBag diagnostics,
ImmutableArray<BoundExpression> tupleArguments,
DiagnosticBag diagnostics,
ImmutableArray<BoundExpression> tupleArguments,
ImmutableArray<TypeSymbol> targetElementTypes)
{
var argLength = tupleArguments.Length;
Expand All @@ -2820,7 +2823,7 @@ private void GenerateImplicitConversionErrorsForTupleLiteralArguments(
// By the time we get here we have done analysis and know we have failed the cast in general, and diagnostics collected in the process is already in the bag.
// The only thing left is to form a diagnostics about the actually failing conversion(s).
// This whole method does not itself collect any usesite diagnostics. Its only purpose is to produce an error better than "conversion failed here"
HashSet <DiagnosticInfo> usDiagsUnused = null;
HashSet<DiagnosticInfo> usDiagsUnused = null;

for (int i = 0; i < targetElementTypes.Length; i++)
{
Expand Down Expand Up @@ -3209,7 +3212,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 BoundDefaultExpression(interactiveInitializerMethod.GetNonNullSyntaxNode(), interactiveInitializerMethod.ResultType);
}
}

Expand Down Expand Up @@ -3541,7 +3544,7 @@ private BoundExpression BindCatchFilter(CatchFilterClauseSyntax filter, Diagnost
{
Error(diagnostics, ErrorCode.WRN_FilterIsConstant, filter.FilterExpression);
}

return boundFilter;
}

Expand Down
27 changes: 17 additions & 10 deletions src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -500,21 +500,28 @@ 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;
}
}

if ((object)collectionExprType == null) // There's no way to enumerate something without a type.
{
// The null literal was caught above, so anything else with a null type is a method group or anonymous function
diagnostics.Add(ErrorCode.ERR_AnonMethGrpInForEach, _syntax.Expression.Location, collectionExpr.Display);
// CONSIDER: dev10 also reports ERR_ForEachMissingMember (i.e. failed pattern match).

if (collectionExpr.Kind == BoundKind.DefaultExpression)
{
diagnostics.Add(ErrorCode.ERR_DefaultLiteralNotValid, _syntax.Expression.Location);
}
else
{
// The null and default literals were caught above, so anything else with a null type is a method group or anonymous function
diagnostics.Add(ErrorCode.ERR_AnonMethGrpInForEach, _syntax.Expression.Location, collectionExpr.Display);
// CONSIDER: dev10 also reports ERR_ForEachMissingMember (i.e. failed pattern match).
}
return false;
}

Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ private BoundPatternSwitchLabel BindPatternSwitchSectionLabel(
hasErrors = true;
}

if (caseLabelSyntax.Value.Kind() == SyntaxKind.DefaultLiteralExpression)
{
diagnostics.Add(ErrorCode.WRN_DefaultInSwitch, caseLabelSyntax.Value.Location);
}

// Until we've determined whether or not the switch label is reachable, we assume it
// is. The caller updates isReachable after determining if the label is subsumed.
const bool isReachable = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ private static void AssertTrivialConversion(ConversionKind kind)
case ConversionKind.ImplicitThrow:
case ConversionKind.AnonymousFunction:
case ConversionKind.Boxing:
case ConversionKind.NullLiteral:
case ConversionKind.DefaultOrNullLiteral:
case ConversionKind.NullToPointer:
case ConversionKind.PointerToVoid:
case ConversionKind.PointerToPointer:
Expand Down Expand Up @@ -219,7 +219,7 @@ internal static Conversion GetTrivialConversion(ConversionKind kind)
internal static Conversion ImplicitThrow => new Conversion(ConversionKind.ImplicitThrow);
internal static Conversion AnonymousFunction => new Conversion(ConversionKind.AnonymousFunction);
internal static Conversion Boxing => new Conversion(ConversionKind.Boxing);
internal static Conversion NullLiteral => new Conversion(ConversionKind.NullLiteral);
internal static Conversion DefaultOrNullLiteral => new Conversion(ConversionKind.DefaultOrNullLiteral);
internal static Conversion NullToPointer => new Conversion(ConversionKind.NullToPointer);
internal static Conversion PointerToVoid => new Conversion(ConversionKind.PointerToVoid);
internal static Conversion PointerToPointer => new Conversion(ConversionKind.PointerToPointer);
Expand Down Expand Up @@ -593,16 +593,16 @@ public bool IsUnboxing
}

/// <summary>
/// Returns true if the conversion is an implicit null literal conversion.
/// Returns true if the conversion is an implicit null or default literal conversion.
/// </summary>
/// <remarks>
/// Null literal conversions are described in section 6.1.5 of the C# language specification.
/// Null or default literal conversions are described in section 6.1.5 of the C# language specification.
/// </remarks>
public bool IsNullLiteral
{
get
{
return Kind == ConversionKind.NullLiteral;
return Kind == ConversionKind.DefaultOrNullLiteral;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal enum ConversionKind : byte
ExplicitTupleLiteral,
ExplicitTuple,
ImplicitNullable,
NullLiteral,
DefaultOrNullLiteral,
ImplicitReference,
Boxing,
PointerToVoid,
Expand Down
Loading

0 comments on commit d0d07e5

Please sign in to comment.