diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 42acd2a298e5e..eadedbb274034 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -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 @@ -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, @@ -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; } @@ -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: diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index ea6f256febbc0..a7591f24219fb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -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. @@ -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(); @@ -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); } /// @@ -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); } @@ -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); } @@ -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); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 9033752852dad..01444f1e0d683 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -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 diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index b5da656d0138e..c783ac88ae433 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -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 @@ -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); @@ -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) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 7864640018f4c..ea035f8778d3c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -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: diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index 725e109eb88c8..7a540dfa613b3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -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()); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index b68114729b245..af58cb970d4e1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -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); } @@ -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); @@ -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); @@ -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) @@ -2809,8 +2812,8 @@ protected void GenerateImplicitConversionError( } private void GenerateImplicitConversionErrorsForTupleLiteralArguments( - DiagnosticBag diagnostics, - ImmutableArray tupleArguments, + DiagnosticBag diagnostics, + ImmutableArray tupleArguments, ImmutableArray targetElementTypes) { var argLength = tupleArguments.Length; @@ -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 usDiagsUnused = null; + HashSet usDiagsUnused = null; for (int i = 0; i < targetElementTypes.Length; i++) { @@ -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); } } @@ -3541,7 +3544,7 @@ private BoundExpression BindCatchFilter(CatchFilterClauseSyntax filter, Diagnost { Error(diagnostics, ErrorCode.WRN_FilterIsConstant, filter.FilterExpression); } - + return boundFilter; } diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 53e5943533178..e983843d28939 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -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; } diff --git a/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs b/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs index b57087d4e38ac..aa86e537ce024 100644 --- a/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs @@ -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; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index 1a5afbc0a108a..f93816b1d0ca4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -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: @@ -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); @@ -593,16 +593,16 @@ public bool IsUnboxing } /// - /// Returns true if the conversion is an implicit null literal conversion. + /// Returns true if the conversion is an implicit null or default literal conversion. /// /// - /// 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. /// public bool IsNullLiteral { get { - return Kind == ConversionKind.NullLiteral; + return Kind == ConversionKind.DefaultOrNullLiteral; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs index 20221bb242f52..559b2882e043e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs @@ -18,7 +18,7 @@ internal enum ConversionKind : byte ExplicitTupleLiteral, ExplicitTuple, ImplicitNullable, - NullLiteral, + DefaultOrNullLiteral, ImplicitReference, Boxing, PointerToVoid, diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs index c0584b9d9ab2f..462f59ccbeb2c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs @@ -30,7 +30,7 @@ public static bool IsImplicitConversion(this ConversionKind conversionKind) case ConversionKind.ImplicitEnumeration: case ConversionKind.ImplicitThrow: case ConversionKind.ImplicitNullable: - case ConversionKind.NullLiteral: + case ConversionKind.DefaultOrNullLiteral: case ConversionKind.ImplicitReference: case ConversionKind.Boxing: case ConversionKind.ImplicitDynamic: diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 6eda21576a669..d09ff8b156be1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -35,7 +35,7 @@ protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth) protected abstract Conversion GetExplicitTupleLiteralConversion(BoundTupleLiteral source, TypeSymbol destination, ref HashSet useSiteDiagnostics, bool forCast); - internal AssemblySymbol CorLibrary { get { return corLibrary; } } + internal AssemblySymbol CorLibrary { get { return corLibrary; } } /// /// Determines if the source expression is convertible to the destination type via @@ -802,6 +802,14 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi } break; + case BoundKind.DefaultExpression: + var defaultExpression = (BoundDefaultExpression)sourceExpression; + if ((object)defaultExpression.Type == null) + { + return Conversion.DefaultOrNullLiteral; + } + break; + case BoundKind.TupleLiteral: var tupleConversion = ClassifyImplicitTupleLiteralConversion((BoundTupleLiteral)sourceExpression, destination, ref useSiteDiagnostics); if (tupleConversion.Exists) @@ -855,7 +863,7 @@ private static Conversion ClassifyNullLiteralConversion(BoundExpression source, { // The spec defines a "null literal conversion" specifically as a conversion from // null to nullable type. - return Conversion.NullLiteral; + return Conversion.DefaultOrNullLiteral; } // SPEC: An implicit conversion exists from the null literal to any reference type. @@ -965,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; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs index 36cd8cf1c7ba5..d99840649fbbe 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs @@ -594,7 +594,7 @@ private static bool IsEncompassingImplicitConversionKind(ConversionKind kind) case ConversionKind.PointerToVoid: // Added to spec in Roslyn timeframe. - case ConversionKind.NullLiteral: + case ConversionKind.DefaultOrNullLiteral: // updated to include "default" in C# 7.1 case ConversionKind.NullToPointer: // Added for C# 7. diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs index d05cc5408c67a..a05986b0606a6 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs @@ -608,6 +608,11 @@ private BoundSwitchLabel BindSwitchSectionLabel(SwitchLabelSyntax node, Binder s hasErrors = true; } + if (caseLabelSyntax.Value.Kind() == SyntaxKind.DefaultLiteralExpression) + { + diagnostics.Add(ErrorCode.WRN_DefaultInSwitch, caseLabelSyntax.Value.Location); + } + // LabelSymbols for all the switch case labels are created by BuildLabels(). // Fetch the matching switch case label symbols break; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index 8e3ccdf8a86be..f02545f32360b 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs @@ -460,7 +460,7 @@ public override Symbol ExpressionSymbol } } - internal partial class BoundDefaultOperator + internal partial class BoundDefaultExpression { public override ConstantValue ConstantValue { diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs index 01e5cf371e9d4..78f0a2bb44e26 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs @@ -14,6 +14,11 @@ public static bool IsLiteralNull(this BoundExpression node) return node.Kind == BoundKind.Literal && node.ConstantValue.Discriminator == ConstantValueTypeDiscriminator.Null; } + public static bool IsLiteralDefault(this BoundExpression node) + { + return node.Kind == BoundKind.DefaultExpression && node.Syntax.Kind() == SyntaxKind.DefaultLiteralExpression; + } + // returns true when expression has no side-effects and produces // default value (null, zero, false, default(T) ...) // @@ -22,7 +27,7 @@ public static bool IsLiteralNull(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.DefaultExpression) { return true; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 9368b2426fb8c..3f6fe70b8906d 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -517,7 +517,9 @@ - + + + diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundTreeVisitors.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundTreeVisitors.cs index f84414e9fb59e..13143445ad4d8 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundTreeVisitors.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundTreeVisitors.cs @@ -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.DefaultExpression: + return VisitDefaultExpression(node as BoundDefaultExpression, arg); case BoundKind.IsOperator: return VisitIsOperator(node as BoundIsOperator, arg); case BoundKind.AsOperator: diff --git a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs index 82ff2dab972c1..6f7d940def080 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs @@ -523,10 +523,10 @@ public static BoundBlock SynthesizedNoLocals(SyntaxNode syntax, params BoundStat } } - internal partial class BoundDefaultOperator + internal partial class BoundDefaultExpression { - public BoundDefaultOperator(SyntaxNode syntax, TypeSymbol type) - : this(syntax, type.GetDefaultValue(), type) + public BoundDefaultExpression(SyntaxNode syntax, TypeSymbol type, bool hasErrors = false) + : this(syntax, type.GetDefaultValue(), type, hasErrors) { } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 28c2349fb1cd1..7ca2b1c82ba98 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -738,7 +738,7 @@ Semantics.ConversionKind IConversionExpression.ConversionKind case CSharp.ConversionKind.ImplicitConstant: case CSharp.ConversionKind.IntegerToPointer: case CSharp.ConversionKind.IntPtr: - case CSharp.ConversionKind.NullLiteral: + case CSharp.ConversionKind.DefaultOrNullLiteral: case CSharp.ConversionKind.NullToPointer: case CSharp.ConversionKind.PointerToInteger: case CSharp.ConversionKind.PointerToPointer: @@ -940,7 +940,7 @@ public override TResult Accept(OperationVisitor OperationKind.DefaultValueExpression; @@ -1945,7 +1945,7 @@ public override TResult Accept(OperationVisitor OperationKind.None; diff --git a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs index efd0dfb657dc6..279c29f1f8d3f 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs @@ -135,4 +135,12 @@ public override object Display get { throw ExceptionUtilities.Unreachable; } } } + + internal partial class BoundDefaultExpression + { + public override object Display + { + get { return (object)this.Type ?? "default"; } + } + } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index e003a0688b16a..073b62665fac5 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -449,7 +449,7 @@ internal static string ERR_AnonymousTypeNotAvailable { } /// - /// Looks up a localized string similar to Cannot assign {0} to anonymous type property. + /// Looks up a localized string similar to Cannot assign '{0}' to anonymous type property. /// internal static string ERR_AnonymousTypePropertyAssignedBadValue { get { @@ -1357,6 +1357,15 @@ internal static string ERR_BadDynamicMethodArg { } } + /// + /// Looks up a localized string similar to Cannot use a default literal as an argument to a dynamically dispatched operation.. + /// + internal static string ERR_BadDynamicMethodArgDefaultLiteral { + get { + return ResourceManager.GetString("ERR_BadDynamicMethodArgDefaultLiteral", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type.. /// @@ -3202,6 +3211,15 @@ internal static string ERR_DeconstructWrongCardinality { } } + /// + /// Looks up a localized string similar to Use of default literal is not valid in this context. + /// + internal static string ERR_DefaultLiteralNotValid { + get { + return ResourceManager.GetString("ERR_DefaultLiteralNotValid", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot specify the DefaultMember attribute on a type containing an indexer. /// @@ -4157,7 +4175,7 @@ internal static string ERR_ExpressionTreeContainsAssignment { } /// - /// Looks up a localized string similar to An expression tree lambda may not contain a coalescing operator with a null literal left-hand side. + /// Looks up a localized string similar to An expression tree lambda may not contain a coalescing operator with a null or default literal left-hand side. /// internal static string ERR_ExpressionTreeContainsBadCoalesce { get { @@ -8837,7 +8855,7 @@ internal static string ERR_SubexpressionNotInNameof { } /// - /// Looks up a localized string similar to The switch expression must be a value; found {0}.. + /// Looks up a localized string similar to The switch expression must be a value; found '{0}'.. /// internal static string ERR_SwitchExpressionValueExpected { get { @@ -9935,6 +9953,15 @@ internal static string IDS_FeatureDefault { } } + /// + /// Looks up a localized string similar to default literal. + /// + internal static string IDS_FeatureDefaultLiteral { + get { + return ResourceManager.GetString("IDS_FeatureDefaultLiteral", resourceCulture); + } + } + /// /// Looks up a localized string similar to dictionary initializer. /// @@ -12167,6 +12194,24 @@ internal static string WRN_DebugFullNameTooLong_Title { } } + /// + /// Looks up a localized string similar to Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate.. + /// + internal static string WRN_DefaultInSwitch { + get { + return ResourceManager.GetString("WRN_DefaultInSwitch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate.. + /// + internal static string WRN_DefaultInSwitch_Title { + get { + return ResourceManager.GetString("WRN_DefaultInSwitch_Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to The default value specified for parameter '{0}' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 5ec2d5eb6a59f..8e9c62cdbf6f6 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -207,6 +207,9 @@ default operator + + default literal + nullable types @@ -846,6 +849,9 @@ Use of null is not valid in this context + + Use of default literal is not valid in this context + The 'this' object cannot be used before all of its fields are assigned to @@ -2136,7 +2142,7 @@ If such a class is used as a base class and if the deriving class defines a dest No best type found for implicitly-typed array - Cannot assign {0} to anonymous type property + Cannot assign '{0}' to anonymous type property An expression tree may not contain a base access @@ -2181,7 +2187,7 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot use local variable '{0}' before it is declared. The declaration of the local variable hides the field '{1}'. - An expression tree lambda may not contain a coalescing operator with a null literal left-hand side + An expression tree lambda may not contain a coalescing operator with a null or default literal left-hand side Identifier expected @@ -4880,7 +4886,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Deconstruct assignment requires an expression with a type on the right-hand-side. - The switch expression must be a value; found {0}. + The switch expression must be a value; found '{0}'. The switch case has already been handled by a previous case. @@ -5020,6 +5026,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ It is not legal to use the type 'dynamic' in a pattern. + + Cannot use a default literal as an argument to a dynamically dispatched operation. + Provided documentation mode is unsupported or invalid: '{0}'. @@ -5044,6 +5053,12 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Compiler version: '{0}'. Language version: {1}. + + Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. + + + Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. + A tuple may not contain a value of type 'void'. diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 72b6eef8ed49b..1c2d421f538fe 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -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.DefaultExpression: + EmitDefaultExpression((BoundDefaultExpression)expression, used); break; case BoundKind.TypeOfOperator: @@ -2637,7 +2637,7 @@ private void EmitDefaultValue(TypeSymbol type, bool used, SyntaxNode syntaxNode) } } - private void EmitDefaultExpression(BoundDefaultOperator expression, bool used) + private void EmitDefaultExpression(BoundDefaultExpression expression, bool used) { Debug.Assert(expression.Type.SpecialType == SpecialType.System_Decimal || expression.Type.GetDefaultValue() == null, "constant should be set on this expression"); @@ -3009,8 +3009,9 @@ private TypeSymbol StackMergeType(BoundExpression expr) case BoundKind.Conversion: var conversion = (BoundConversion)expr; var conversionKind = conversion.ConversionKind; - if (conversionKind.IsImplicitConversion() && - conversionKind != ConversionKind.NullLiteral) + Debug.Assert(conversionKind != ConversionKind.DefaultOrNullLiteral); + + if (conversionKind.IsImplicitConversion()) { Debug.Assert(conversionKind != ConversionKind.MethodGroup); return StackMergeType(conversion.Operand); diff --git a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs index 9ebf246212b58..d847d9967a144 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs @@ -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 BoundDefaultExpression(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); } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index fc67422ba323b..c457ba5295ef4 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1473,5 +1473,9 @@ internal enum ErrorCode ERR_FeatureNotAvailableInVersion7_1 = 8302, ERR_LanguageVersionCannotHaveLeadingZeroes = 8303, ERR_CompilerAndLanguageVersion = 8304, + + ERR_BadDynamicMethodArgDefaultLiteral = 9000, + ERR_DefaultLiteralNotValid = 9001, + WRN_DefaultInSwitch = 9002, } } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 553f5efba4b5c..d6e49bb9177b8 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -317,6 +317,7 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_AlignmentMagnitude: case ErrorCode.WRN_AttributeIgnoredWhenPublicSigning: case ErrorCode.WRN_TupleLiteralNameMismatch: + case ErrorCode.WRN_DefaultInSwitch: return 1; default: return 0; diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index db433a2c5b01e..fa798c7c8cb2c 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -127,6 +127,7 @@ internal enum MessageID IDS_FeatureExpressionBodiedAccessor = MessageBase + 12715, IDS_FeatureExpressionBodiedDeOrConstructor = MessageBase + 12716, IDS_ThrowExpression = MessageBase + 12717, + IDS_FeatureDefaultLiteral = MessageBase + 12718, } // Message IDs may refer to strings that need to be localized. @@ -183,6 +184,10 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // Checks are in the LanguageParser unless otherwise noted. switch (feature) { + // C# 7.1 features. + case MessageID.IDS_FeatureDefaultLiteral: + return LanguageVersion.CSharp7_1; + // C# 7 features. case MessageID.IDS_FeatureBinaryLiteral: case MessageID.IDS_FeatureDigitSeparator: diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs index 80c1e3383189d..6a3d21995d5ec 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs @@ -608,7 +608,7 @@ internal static bool WriteConsideredUse(TypeSymbol type, BoundExpression value) } return WriteConsideredUse(null, boundConversion.Operand); } - case BoundKind.DefaultOperator: + case BoundKind.DefaultExpression: return false; case BoundKind.ObjectCreationExpression: var init = (BoundObjectCreationExpression)value; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs index f5855486af4ec..275b6e303a4c6 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs @@ -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 BoundDefaultExpression(method.GetNonNullSyntaxNode(), submissionResultType); var newStatements = block.Statements.Add(new BoundReturnStatement(trailingExpression.Syntax, RefKind.None, trailingExpression)); block = new BoundBlock(block.Syntax, ImmutableArray.Empty, newStatements) { WasCompilerGenerated = true }; #if DEBUG diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs index 9c25ce62150a4..d8c5da4691e78 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs @@ -2497,7 +2497,7 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no return null; } - public override BoundNode VisitDefaultOperator(BoundDefaultOperator node) + public override BoundNode VisitDefaultExpression(BoundDefaultExpression node) { return null; } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index c83e7a372a53a..375c8a34e8a71 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -59,7 +59,7 @@ internal enum BoundKind: byte SourceDocumentIndex, MethodInfo, FieldInfo, - DefaultOperator, + DefaultExpression, IsOperator, AsOperator, SizeOfOperator, @@ -1764,16 +1764,16 @@ public BoundFieldInfo Update(FieldSymbol field, MethodSymbol getFieldFromHandle, } } - internal sealed partial class BoundDefaultOperator : BoundExpression + internal sealed partial class BoundDefaultExpression : BoundExpression { - public BoundDefaultOperator(SyntaxNode syntax, ConstantValue constantValueOpt, TypeSymbol type, bool hasErrors) - : base(BoundKind.DefaultOperator, syntax, type, hasErrors) + public BoundDefaultExpression(SyntaxNode syntax, ConstantValue constantValueOpt, TypeSymbol type, bool hasErrors) + : base(BoundKind.DefaultExpression, syntax, type, hasErrors) { this.ConstantValueOpt = constantValueOpt; } - public BoundDefaultOperator(SyntaxNode syntax, ConstantValue constantValueOpt, TypeSymbol type) - : base(BoundKind.DefaultOperator, syntax, type) + public BoundDefaultExpression(SyntaxNode syntax, ConstantValue constantValueOpt, TypeSymbol type) + : base(BoundKind.DefaultExpression, syntax, type) { this.ConstantValueOpt = constantValueOpt; } @@ -1783,14 +1783,14 @@ public BoundDefaultOperator(SyntaxNode syntax, ConstantValue constantValueOpt, T public override BoundNode Accept(BoundTreeVisitor visitor) { - return visitor.VisitDefaultOperator(this); + return visitor.VisitDefaultExpression(this); } - public BoundDefaultOperator Update(ConstantValue constantValueOpt, TypeSymbol type) + public BoundDefaultExpression Update(ConstantValue constantValueOpt, TypeSymbol type) { if (constantValueOpt != this.ConstantValueOpt || type != this.Type) { - var result = new BoundDefaultOperator(this.Syntax, constantValueOpt, type, this.HasErrors); + var result = new BoundDefaultExpression(this.Syntax, constantValueOpt, type, this.HasErrors); result.WasCompilerGenerated = this.WasCompilerGenerated; return result; } @@ -6150,8 +6150,8 @@ internal R VisitInternal(BoundNode node, A arg) return VisitMethodInfo(node as BoundMethodInfo, arg); case BoundKind.FieldInfo: return VisitFieldInfo(node as BoundFieldInfo, arg); - case BoundKind.DefaultOperator: - return VisitDefaultOperator(node as BoundDefaultOperator, arg); + case BoundKind.DefaultExpression: + return VisitDefaultExpression(node as BoundDefaultExpression, arg); case BoundKind.IsOperator: return VisitIsOperator(node as BoundIsOperator, arg); case BoundKind.AsOperator: @@ -6534,7 +6534,7 @@ public virtual R VisitFieldInfo(BoundFieldInfo node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitDefaultOperator(BoundDefaultOperator node, A arg) + public virtual R VisitDefaultExpression(BoundDefaultExpression node, A arg) { return this.DefaultVisit(node, arg); } @@ -7134,7 +7134,7 @@ public virtual BoundNode VisitFieldInfo(BoundFieldInfo node) { return this.DefaultVisit(node); } - public virtual BoundNode VisitDefaultOperator(BoundDefaultOperator node) + public virtual BoundNode VisitDefaultExpression(BoundDefaultExpression node) { return this.DefaultVisit(node); } @@ -7771,7 +7771,7 @@ public override BoundNode VisitFieldInfo(BoundFieldInfo node) { return null; } - public override BoundNode VisitDefaultOperator(BoundDefaultOperator node) + public override BoundNode VisitDefaultExpression(BoundDefaultExpression node) { return null; } @@ -8576,7 +8576,7 @@ public override BoundNode VisitFieldInfo(BoundFieldInfo node) TypeSymbol type = this.VisitType(node.Type); return node.Update(node.Field, node.GetFieldFromHandle, type); } - public override BoundNode VisitDefaultOperator(BoundDefaultOperator node) + public override BoundNode VisitDefaultExpression(BoundDefaultExpression node) { TypeSymbol type = this.VisitType(node.Type); return node.Update(node.ConstantValueOpt, type); @@ -9627,9 +9627,9 @@ public override TreeDumperNode VisitFieldInfo(BoundFieldInfo node, object arg) } ); } - public override TreeDumperNode VisitDefaultOperator(BoundDefaultOperator node, object arg) + public override TreeDumperNode VisitDefaultExpression(BoundDefaultExpression node, object arg) { - return new TreeDumperNode("defaultOperator", null, new TreeDumperNode[] + return new TreeDumperNode("defaultExpression", null, new TreeDumperNode[] { new TreeDumperNode("constantValueOpt", node.ConstantValueOpt, null), new TreeDumperNode("type", node.Type, null) diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index e891c3e0dedf5..7c4222d7ae012 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -173,6 +173,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_AlignmentMagnitude: case ErrorCode.WRN_AttributeIgnoredWhenPublicSigning: case ErrorCode.WRN_TupleLiteralNameMismatch: + case ErrorCode.WRN_DefaultInSwitch: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs index 84c90857ad236..c8c3da2f547f0 100644 --- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs @@ -38389,6 +38389,7 @@ public LiteralExpressionSyntax LiteralExpression(SyntaxKind kind, SyntaxToken to case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: case SyntaxKind.NullLiteralExpression: + case SyntaxKind.DefaultLiteralExpression: break; default: throw new ArgumentException("kind"); @@ -38405,6 +38406,7 @@ public LiteralExpressionSyntax LiteralExpression(SyntaxKind kind, SyntaxToken to case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: case SyntaxKind.NullKeyword: + case SyntaxKind.DefaultKeyword: break; default: throw new ArgumentException("token"); @@ -45300,6 +45302,7 @@ public static LiteralExpressionSyntax LiteralExpression(SyntaxKind kind, SyntaxT case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: case SyntaxKind.NullLiteralExpression: + case SyntaxKind.DefaultLiteralExpression: break; default: throw new ArgumentException("kind"); @@ -45316,6 +45319,7 @@ public static LiteralExpressionSyntax LiteralExpression(SyntaxKind kind, SyntaxT case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: case SyntaxKind.NullKeyword: + case SyntaxKind.DefaultKeyword: break; default: throw new ArgumentException("token"); diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs index f7d33f082806a..ba3ecdfb3cecb 100644 --- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs @@ -5152,6 +5152,7 @@ public static LiteralExpressionSyntax LiteralExpression(SyntaxKind kind, SyntaxT case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: case SyntaxKind.NullLiteralExpression: + case SyntaxKind.DefaultLiteralExpression: break; default: throw new ArgumentException("kind"); @@ -5165,6 +5166,7 @@ public static LiteralExpressionSyntax LiteralExpression(SyntaxKind kind, SyntaxT case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: case SyntaxKind.NullKeyword: + case SyntaxKind.DefaultKeyword: break; default: throw new ArgumentException("token"); @@ -5197,6 +5199,8 @@ private static SyntaxKind GetLiteralExpressionTokenKind(SyntaxKind kind) return SyntaxKind.FalseKeyword; case SyntaxKind.NullLiteralExpression: return SyntaxKind.NullKeyword; + case SyntaxKind.DefaultLiteralExpression: + return SyntaxKind.DefaultKeyword; default: throw new ArgumentOutOfRangeException(); } diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index 0b6fefa4282d2..f59bb4bd9bac7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -596,7 +596,7 @@ public override BoundNode VisitNameOfOperator(BoundNameOfOperator node) public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { - if (_inExpressionLambda && node.LeftOperand.IsLiteralNull()) + if (_inExpressionLambda && (node.LeftOperand.IsLiteralNull() || node.LeftOperand.IsLiteralDefault())) { Error(ErrorCode.ERR_ExpressionTreeContainsBadCoalesce, node.LeftOperand); } diff --git a/src/Compilers/CSharp/Portable/Lowering/Extensions.cs b/src/Compilers/CSharp/Portable/Lowering/Extensions.cs index 188a33818c2b2..1947753eed507 100644 --- a/src/Compilers/CSharp/Portable/Lowering/Extensions.cs +++ b/src/Compilers/CSharp/Portable/Lowering/Extensions.cs @@ -84,7 +84,7 @@ public static bool NullableNeverHasValue(this BoundExpression expr) } // "default(int?)" never has a value. - if (expr.Kind == BoundKind.DefaultOperator) + if (expr.Kind == BoundKind.DefaultExpression) { return true; } @@ -101,8 +101,8 @@ public static bool NullableNeverHasValue(this BoundExpression expr) var conversion = (BoundConversion)expr; switch (conversion.ConversionKind) { - case ConversionKind.NullLiteral: - // Any "null literal conversion" is a conversion from the literal null to + case ConversionKind.DefaultOrNullLiteral: + // Any "null literal conversion" is a conversion from the literals null/default to // a nullable value type; obviously it never has a value. return true; case ConversionKind.ImplicitNullable: diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/ExpressionLambdaRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/ExpressionLambdaRewriter.cs index 59056599ec574..aae78f2c67e20 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/ExpressionLambdaRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/ExpressionLambdaRewriter.cs @@ -231,7 +231,7 @@ private BoundExpression VisitExpressionWithoutStackGuard(BoundExpression node) case BoundKind.UnaryOperator: return VisitUnaryOperator((BoundUnaryOperator)node); - case BoundKind.DefaultOperator: + case BoundKind.DefaultExpression: case BoundKind.HostObjectMemberReference: case BoundKind.Literal: case BoundKind.Local: @@ -456,7 +456,7 @@ private static BoundExpression DemoteEnumOperand(BoundExpression operand) var conversion = (BoundConversion)operand; if (!conversion.ConversionKind.IsUserDefinedConversion() && conversion.ConversionKind.IsImplicitConversion() && - conversion.ConversionKind != ConversionKind.NullLiteral && + conversion.ConversionKind != ConversionKind.DefaultOrNullLiteral && conversion.Type.StrippedType().IsEnumType()) { operand = conversion.Operand; @@ -647,7 +647,7 @@ private BoundExpression VisitConversion(BoundConversion node) var e1 = Convert(Visit(node.Operand), node.Operand.Type, intermediate, node.Checked, false); return Convert(e1, intermediate, node.Type, node.Checked, false); } - case ConversionKind.NullLiteral: + case ConversionKind.DefaultOrNullLiteral: return Convert(Constant(_bound.Null(_objectType)), _objectType, node.Type, false, node.ExplicitCastInCode); default: return Convert(Visit(node.Operand), node.Operand.Type, node.Type, node.Checked, node.ExplicitCastInCode); diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs index 7eab12705e64e..4c763830da725 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs @@ -639,7 +639,7 @@ private BoundNode IntroduceFrame(BoundNode node, LambdaFrame frame, Func.Empty, sideEffects: ImmutableArray.Create(sideEffect), - value: new BoundDefaultOperator(syntax, null, type), + value: new BoundDefaultExpression(syntax, null, type), type: type); } @@ -1302,7 +1302,7 @@ private BoundExpression LowerLiftedBinaryArithmeticOperator( BoundExpression consequence = MakeLiftedBinaryOperatorConsequence(syntax, kind, callX_GetValueOrDefault, callY_GetValueOrDefault, type, method); // default(R?) - BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); + BoundExpression alternative = new BoundDefaultExpression(syntax, null, type); // tempX.HasValue & tempY.HasValue ? // new R?(tempX.GetValueOrDefault() OP tempY.GetValueOrDefault()) : @@ -1478,7 +1478,7 @@ private BoundExpression MakeNewNullableBoolean(SyntaxNode syntax, bool? value) NamedTypeSymbol nullableBoolType = nullableType.Construct(boolType); if (value == null) { - return new BoundDefaultOperator(syntax, null, nullableBoolType); + return new BoundDefaultExpression(syntax, null, nullableBoolType); } return new BoundObjectCreationExpression( @@ -1521,7 +1521,7 @@ private BoundExpression OptimizeLiftedBooleanOperatorOneNull( BoundExpression alwaysNull = leftAlwaysNull ? left : right; BoundExpression notAlwaysNull = leftAlwaysNull ? right : left; BoundExpression neverNull = NullableAlwaysHasValue(notAlwaysNull); - BoundExpression nullBool = new BoundDefaultOperator(syntax, null, alwaysNull.Type); + BoundExpression nullBool = new BoundDefaultExpression(syntax, null, alwaysNull.Type); if (neverNull != null) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 23819150c9849..3ee96a545797e 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -300,7 +300,7 @@ private static bool IsSafeForReordering(BoundExpression expression, RefKind kind case ConversionKind.AnonymousFunction: case ConversionKind.ImplicitConstant: case ConversionKind.MethodGroup: - case ConversionKind.NullLiteral: + case ConversionKind.DefaultOrNullLiteral: return true; case ConversionKind.Boxing: @@ -994,14 +994,14 @@ private BoundExpression GetDefaultParameterValue(SyntaxNode syntax, ParameterSym else { // The argument to M([Optional] int x) becomes default(int) - defaultValue = new BoundDefaultOperator(syntax, parameterType); + defaultValue = new BoundDefaultExpression(syntax, parameterType); } } else if (defaultConstantValue.IsNull && parameterType.IsValueType) { // We have something like M(int? x = null) or M(S x = default(S)), // so replace the argument with default(int?). - defaultValue = new BoundDefaultOperator(syntax, parameterType); + defaultValue = new BoundDefaultExpression(syntax, parameterType); } else if (parameterType.IsNullableType()) { @@ -1057,20 +1057,20 @@ private BoundExpression GetDefaultParameterSpecial(SyntaxNode syntax, ParameterS if (parameter.IsMarshalAsObject) { // default(object) - defaultValue = new BoundDefaultOperator(syntax, parameter.Type); + defaultValue = new BoundDefaultExpression(syntax, parameter.Type); } else if (parameter.IsIUnknownConstant) { // new UnknownWrapper(default(object)) var methodSymbol = (MethodSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_UnknownWrapper__ctor); - var argument = new BoundDefaultOperator(syntax, parameter.Type); + var argument = new BoundDefaultExpression(syntax, parameter.Type); defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, argument); } else if (parameter.IsIDispatchConstant) { // new DispatchWrapper(default(object)) var methodSymbol = (MethodSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_DispatchWrapper__ctor); - var argument = new BoundDefaultOperator(syntax, parameter.Type); + var argument = new BoundDefaultExpression(syntax, parameter.Type); defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, argument); } else diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 54ee10bb30ec9..3ff46b9d2e426 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -180,7 +180,7 @@ private BoundExpression MakeConversionNode( if (NullableNeverHasValue(rewrittenOperand)) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultExpression(syntax, rewrittenType); } BoundExpression nullableValue = NullableAlwaysHasValue(rewrittenOperand); @@ -192,10 +192,10 @@ private BoundExpression MakeConversionNode( } break; - case ConversionKind.NullLiteral: + case ConversionKind.DefaultOrNullLiteral: if (!_inExpressionLambda || !explicitCastInCode) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultExpression(syntax, rewrittenType); } break; @@ -204,7 +204,7 @@ private BoundExpression MakeConversionNode( case ConversionKind.ExplicitReference: if (rewrittenOperand.IsDefaultValue() && (!_inExpressionLambda || !explicitCastInCode)) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultExpression(syntax, rewrittenType); } break; @@ -222,7 +222,7 @@ private BoundExpression MakeConversionNode( case ConversionKind.ExplicitNumeric: if (rewrittenOperand.IsDefaultValue() && (!_inExpressionLambda || !explicitCastInCode)) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultExpression(syntax, rewrittenType); } if (rewrittenType.SpecialType == SpecialType.System_Decimal || rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal) @@ -282,7 +282,7 @@ private BoundExpression MakeConversionNode( rewrittenOperand.IsDefaultValue() && (!_inExpressionLambda || !explicitCastInCode)) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultExpression(syntax, rewrittenType); } if (rewrittenType.SpecialType == SpecialType.System_Decimal) @@ -684,7 +684,7 @@ private static bool NullableNeverHasValue(BoundExpression expression) } // default(int?) never has a value. - if (expression.Kind == BoundKind.DefaultOperator) + if (expression.Kind == BoundKind.DefaultExpression) { return true; } @@ -857,7 +857,7 @@ private BoundExpression RewriteFullyLiftedBuiltInConversion( conversion.UnderlyingConversions[0], type.GetNullableUnderlyingType(), @checked)); - BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); + BoundExpression alternative = new BoundDefaultExpression(syntax, null, type); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, @@ -885,7 +885,7 @@ private BoundExpression OptimizeLiftedUserDefinedConversion( if (NullableNeverHasValue(operand)) { - return new BoundDefaultOperator(syntax, type); + return new BoundDefaultExpression(syntax, type); } // If the converted expression is known to never be null then we can return @@ -914,7 +914,7 @@ private BoundExpression OptimizeLiftedBuiltInConversion( if (NullableNeverHasValue(operand)) { - return new BoundDefaultOperator(syntax, null, type); + return new BoundDefaultExpression(syntax, null, type); } // Second, a trickier optimization. If the conversion is "(T?)(new S?(x))" then @@ -1094,7 +1094,7 @@ private BoundExpression RewriteLiftedUserDefinedConversion( BoundExpression consequence = MakeLiftedUserDefinedConversionConsequence(userDefinedCall, rewrittenType); // default(R?) - BoundExpression alternative = new BoundDefaultOperator(syntax, rewrittenType); + BoundExpression alternative = new BoundDefaultExpression(syntax, rewrittenType); // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?) BoundExpression conditionalExpression = RewriteConditionalOperator( diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs index 847dc1e0c7d5d..a0bb56b340dde 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs @@ -63,7 +63,7 @@ public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpre // replace "new S()" with a default struct ctor with "default(S)" if (node.Constructor.IsDefaultValueTypeConstructor()) { - rewrittenObjectCreation = new BoundDefaultOperator(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type); + rewrittenObjectCreation = new BoundDefaultExpression(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type); } if (!temps.IsDefaultOrEmpty) @@ -185,7 +185,7 @@ private BoundExpression MakeNewT(SyntaxNode syntax, TypeParameterSymbol typePara if (!this.TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Activator__CreateInstance_T, out method)) { - return new BoundDefaultOperator(syntax, null, type: typeParameter, hasErrors: true); + return new BoundDefaultExpression(syntax, null, type: typeParameter, hasErrors: true); } Debug.Assert((object)method != null); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs index f92398fba9dc5..b3c64901f9383 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs @@ -199,7 +199,7 @@ private BoundExpression LowerLiftedUnaryOperator( BoundExpression consequence = GetLiftedUnaryOperatorConsequence(kind, syntax, method, type, call_GetValueOrDefault); // default(R?) - BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); + BoundExpression alternative = new BoundDefaultExpression(syntax, null, type); // temp.HasValue ? // new R?(OP(temp.GetValueOrDefault())) : @@ -233,7 +233,7 @@ private BoundExpression OptimizeLiftedUnaryOperator( { if (NullableNeverHasValue(loweredOperand)) { - return new BoundDefaultOperator(syntax, null, type); + return new BoundDefaultExpression(syntax, null, type); } // Second, another simple optimization. If we know that the operand is never null @@ -636,7 +636,7 @@ private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall); // default(S?) - BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); + BoundExpression alternative = new BoundDefaultExpression(syntax, null, type); // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : @@ -802,7 +802,7 @@ private BoundExpression MakeLiftedDecimalIncDecOperator(SyntaxNode syntax, Binar // new decimal?(op_Inc(x.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, methodCall); // default(decimal?) - BoundExpression alternative = new BoundDefaultOperator(syntax, null, operand.Type); + BoundExpression alternative = new BoundDefaultExpression(syntax, null, operand.Type); // x.HasValue ? new decimal?(op_Inc(x.GetValueOrDefault())) : default(decimal?) return RewriteConditionalOperator(syntax, condition, consequence, alternative, ConstantValue.NotAvailable, operand.Type); diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs index c452b37c94cda..e253ecbace752 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -549,7 +549,7 @@ private BoundExpression HoistExpression( case BoundKind.ThisReference: case BoundKind.BaseReference: - case BoundKind.DefaultOperator: + case BoundKind.DefaultExpression: return expr; default: diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 0dbc1d0966ab9..16059d50d96b7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -1176,7 +1176,7 @@ public BoundExpression Array(TypeSymbol elementType, BoundExpression length) internal BoundExpression Default(TypeSymbol type) { - return new BoundDefaultOperator(Syntax, type) { WasCompilerGenerated = true }; + return new BoundDefaultExpression(Syntax, type) { WasCompilerGenerated = true }; } internal BoundStatement Try( diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index a1635bf748511..32a1a3888c9ad 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -9729,7 +9729,7 @@ private bool IsPossibleOutVarDeclaration() SyntaxToken lastTokenOfType; ScanTypeFlags st = this.ScanType(out lastTokenOfType); - return st != ScanTypeFlags.NotType && this.IsTrueIdentifier(); + return st != ScanTypeFlags.NotType && this.IsTrueIdentifier(); } finally { @@ -9748,16 +9748,23 @@ private TypeOfExpressionSyntax ParseTypeOfExpression() return _syntaxFactory.TypeOfExpression(keyword, openParen, type, closeParen); } - private DefaultExpressionSyntax ParseDefaultExpression() + private ExpressionSyntax ParseDefaultExpression() { var keyword = this.EatToken(); - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var type = this.ParseType(); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - - keyword = CheckFeatureAvailability(keyword, MessageID.IDS_FeatureDefault); + if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken) + { + var openParen = this.EatToken(SyntaxKind.OpenParenToken); + var type = this.ParseType(); + var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - return _syntaxFactory.DefaultExpression(keyword, openParen, type, closeParen); + keyword = CheckFeatureAvailability(keyword, MessageID.IDS_FeatureDefault); + return _syntaxFactory.DefaultExpression(keyword, openParen, type, closeParen); + } + else + { + keyword = CheckFeatureAvailability(keyword, MessageID.IDS_FeatureDefaultLiteral); + return _syntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression, keyword); + } } private SizeOfExpressionSyntax ParseSizeOfExpression() diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index e8e7ada3f7433..ca0ea725229ca 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -1,6 +1,7 @@ Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp7_1 = 701 -> Microsoft.CodeAnalysis.CSharp.LanguageVersion Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts Microsoft.CodeAnalysis.CSharp.SyntaxKind.ConflictMarkerTrivia = 8564 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.DefaultLiteralExpression = 8755 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.MapSpecifiedToEffectiveVersion(this Microsoft.CodeAnalysis.CSharp.LanguageVersion version) -> Microsoft.CodeAnalysis.CSharp.LanguageVersion static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.ToDisplayString(this Microsoft.CodeAnalysis.CSharp.LanguageVersion version) -> string -static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.TryParse(this string version, out Microsoft.CodeAnalysis.CSharp.LanguageVersion result) -> bool +static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.TryParse(this string version, out Microsoft.CodeAnalysis.CSharp.LanguageVersion result) -> bool \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 5460e710a32f9..e467d3d7930ca 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -429,7 +429,7 @@ private static bool IsValidDefaultValue(BoundExpression expression) // Also when valuetype S has a parameterless constructor, // new S() is clearly not a constant expression and should produce an error return (expression.ConstantValue != null) || - (expression.Kind == BoundKind.DefaultOperator) || + (expression.Kind == BoundKind.DefaultExpression) || (expression.Kind == BoundKind.ObjectCreationExpression && IsValidDefaultValue((BoundObjectCreationExpression)expression)); } diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index 3e1a21d85c5dc..7c0a378deab94 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -726,6 +726,7 @@ + @@ -734,6 +735,7 @@ + SyntaxToken representing the keyword corresponding to the kind of the literal expression. diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index 45f227fd7d7b2..34f006a9866a4 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -398,6 +398,7 @@ public enum SyntaxKind : ushort TrueLiteralExpression = 8752, FalseLiteralExpression = 8753, NullLiteralExpression = 8754, + DefaultLiteralExpression = 8755, // primary function expressions TypeOfExpression = 8760, diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs index 926f352288de3..8ed6c65fb17b2 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs @@ -5042,6 +5042,49 @@ static void Main(string[] args) expectedOutput: expectedOutput); } + [Fact] + public void EnumEqualityWithDefault() + { + string source = +@" +using System; +using System.Linq.Expressions; + +namespace ConsoleApplication1 +{ + enum YesNo + { + Yes, + No + } + + class MyType + { + public string Name { get; set; } + public YesNo? YesNo { get; set; } + + public int? Age { get; set; } + } + + class Program + { + static void Main(string[] args) + { + + Expression> expr = (MyType x) => x.YesNo == (YesNo?)default; + Console.WriteLine(expr.Dump()); + } + } + +}"; + string expectedOutput = "Equal(Convert(MemberAccess(Parameter(x Type:ConsoleApplication1.MyType).YesNo Type:System.Nullable`1[ConsoleApplication1.YesNo]) Lifted LiftedToNull Type:System.Nullable`1[System.Int32]) Convert(Convert(Constant(null Type:System.Object) Lifted LiftedToNull Type:System.Nullable`1[ConsoleApplication1.YesNo]) Lifted LiftedToNull Type:System.Nullable`1[System.Int32]) Lifted Type:System.Boolean)"; + CompileAndVerify( + new[] { source, ExpressionTestLibrary }, + new[] { ExpressionAssemblyRef }, + expectedOutput: expectedOutput, + parseOptions: TestOptions.ExperimentalParseOptions); + } + [WorkItem(546618, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546618")] [Fact] public void TildeNullableEnum() diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index d5f0218a4591a..5f03707868e8e 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -80,6 +80,7 @@ + diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs new file mode 100644 index 0000000000000..f59dff07b54fc --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs @@ -0,0 +1,2190 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using System.Linq; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics +{ + [CompilerTrait(CompilerFeature.DefaultLiteral)] + public class DefaultLiteralTests : CompilingTestBase + { + [Fact] + public void TestCSharp7() + { + string source = @" +class C +{ + static void Main() + { + int x = default; + } +} +"; + var comp = CreateStandardCompilation(source); + comp.VerifyDiagnostics( + // (6,17): error CS8107: Feature 'default literal' is not available in C# 7. Please use language version 7.1 or greater. + // int x = default; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "default").WithArguments("default literal", "7.1").WithLocation(6, 17) + ); + } + + [Fact] + public void AssignmentToInt() + { + string source = @" +class C +{ + static void Main() + { + int x = default; + System.Console.Write(x); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().Single(); + Assert.Equal("System.Int32", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Equal("System.Int32", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Equal("0", model.GetConstantValue(def).Value.ToString()); + Assert.True(model.GetConversion(def).IsNullLiteral); + } + + [Fact] + public void AssignmentToThisOnRefType() + { + string source = @" +public class C +{ + public int field; + public C() => this = default; + public static void Main() + { + new C(); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (5,19): error CS1604: Cannot assign to 'this' because it is read-only + // public C() => this = default; + Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(5, 19) + ); + } + + [Fact] + public void AssignmentToThisOnStructType() + { + string source = @" +public struct S +{ + public int field; + public S(int x) => this = default; + public static void Main() + { + new S(1); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().ElementAt(0); + Assert.Equal("default", def.ToString()); + Assert.Equal("S", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Equal("S", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void InAttributeParameter() + { + string source = @" +[Custom(z: default, y: default, x: default)] +class C +{ + [Custom(default, default)] + void M() + { + } +} +public class CustomAttribute : System.Attribute +{ + public CustomAttribute(int x, string y, byte z = 0) { } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + + [Fact] + public void InStringInterpolation() + { + string source = @" +class C +{ + static void Main() + { + System.Console.Write($""({default}) ({null})""); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "() ()"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().ElementAt(0); + Assert.Equal("default", def.ToString()); + Assert.Null(model.GetTypeInfo(def).Type); // Should be given a type. Follow-up issue: https://github.com/dotnet/roslyn/issues/18609 + Assert.Null(model.GetTypeInfo(def).ConvertedType); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.False(model.GetConstantValue(def).HasValue); + Assert.False(model.GetConversion(def).IsNullLiteral); + + var nullSyntax = nodes.OfType().ElementAt(1); + Assert.Equal("null", nullSyntax.ToString()); + Assert.Null(model.GetTypeInfo(nullSyntax).Type); + Assert.Null(model.GetTypeInfo(nullSyntax).ConvertedType); // Should be given a type. Follow-up issue: https://github.com/dotnet/roslyn/issues/18609 + Assert.Null(model.GetSymbolInfo(nullSyntax).Symbol); + } + + [Fact] + public void InUsing() + { + string source = @" +class C +{ + static void Main() + { + using (default) + { + System.Console.Write(""ok""); + } + using (null) { } + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "ok"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().ElementAt(0); + Assert.Equal("default", def.ToString()); + Assert.Null(model.GetTypeInfo(def).Type); + Assert.Null(model.GetTypeInfo(def).ConvertedType); // Should get a type. Follow-up issue: https://github.com/dotnet/roslyn/issues/18609 + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.False(model.GetConstantValue(def).HasValue); + Assert.False(model.GetConversion(def).IsNullLiteral); + + var nullSyntax = nodes.OfType().ElementAt(2); + Assert.Equal("null", nullSyntax.ToString()); + Assert.Null(model.GetTypeInfo(nullSyntax).Type); + Assert.Null(model.GetTypeInfo(nullSyntax).ConvertedType); // Should get a type. Follow-up issue: https://github.com/dotnet/roslyn/issues/18609 + } + + [Fact] + public void CannotAwaitDefault() + { + string source = @" +class C +{ + async System.Threading.Tasks.Task M() + { + await default; + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,9): error CS4001: Cannot await 'default' + // await default; + Diagnostic(ErrorCode.ERR_BadAwaitArgIntrinsic, "await default").WithArguments("default").WithLocation(6, 9) + ); + } + + [Fact] + public void ReturningDefaultFromAsyncMethod() + { + string source = @" +using System.Threading.Tasks; +class C +{ + async Task M2() + { + await Task.Delay(0); + return default; + } +} +"; + + var comp = CreateCompilationWithMscorlib46(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().ElementAt(1); + Assert.Equal("default", def.ToString()); + Assert.Equal("T", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Equal("T", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.False(model.GetConstantValue(def).HasValue); + Assert.True(model.GetConversion(def).IsNullLiteral); + } + + [Fact] + public void AsyncLambda() + { + string source = @" +class C +{ + static void F(System.Threading.Tasks.Task t) { } + + static void M() + { + F(async () => await default); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (8,9): error CS0411: The type arguments for method 'C.F(Task)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F(async () => await default); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("C.F(System.Threading.Tasks.Task)").WithLocation(8, 9) + ); + } + + [Fact] + public void RefReturnValue() + { + string source = @" +class C +{ + ref int M() + { + return default; + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,9): error CS8150: By-value returns may only be used in methods that return by value + // return default; + Diagnostic(ErrorCode.ERR_MustHaveRefReturn, "return").WithLocation(6, 9) + ); + } + + [Fact] + public void BadAssignment() + { + string source = @" +class C +{ + static void M() + { + var x4 = default; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,13): error CS0815: Cannot assign default to an implicitly-typed variable + // var x4 = default; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "x4 = default").WithArguments("default").WithLocation(6, 13) + ); + } + + [Fact] + public void AssignmentToRefType() + { + string source = @" +class C where T : class +{ + static void M() + { + C x1 = default; + int? x2 = default; + dynamic x3 = default; + ITest x5 = default; + T x6 = default; + System.Console.Write($""{x1} {x2} {x3} {x5} {x6}""); + } +} +interface ITest { } +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + + [Fact] + public void AssignmentToStructType() + { + string source = @" +struct S +{ + static void M() + { + S x1 = default; + System.Console.Write(x1); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().Single(); + Assert.Equal("S", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Equal("S", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.False(model.GetConstantValue(def).HasValue); + Assert.True(model.GetConversion(def).IsNullLiteral); + } + + [Fact] + public void AssignmentToGenericType() + { + string source = @" +class C +{ + static void M() + { + T x1 = default; + System.Console.Write(x1); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().Single(); + Assert.Equal("T", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Equal("T", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.False(model.GetConstantValue(def).HasValue); + Assert.True(model.GetConversion(def).IsNullLiteral); + } + + [Fact] + public void AmbiguousMethod() + { + string source = @" +class C +{ + static void Main() + { + M(default); + } + static void M(int x) { } + static void M(string x) { } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,9): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(int)' and 'C.M(string)' + // M(default); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(int)", "C.M(string)").WithLocation(6, 9) + ); + } + + [Fact] + public void MethodWithRefParameters() + { + string source = @" +class C +{ + static void Main() + { + M(default); + } + static void M(string x) { System.Console.Write(x == null ? ""null"" : ""bad""); } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "null"); + } + + [Fact] + public void MethodWithNullableParameters() + { + string source = @" +class C +{ + static void Main() + { + M(default); + } + static void M(int? x) { System.Console.Write(x.HasValue ? ""bad"" : ""null""); } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "null"); + } + + [Fact] + public void CannotInferTypeArg() + { + string source = @" +class C +{ + static void Main() + { + M(default); + } + static void M(T x) { } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,9): error CS0411: The type arguments for method 'C.M(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // M(default); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M").WithArguments("C.M(T)").WithLocation(6, 9) + ); + } + + [Fact] + public void CannotInferTypeArg2() + { + string source = @" +class C +{ + static void Main() + { + M(default, null); + } + static void M(T x, T y) where T : class { } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,9): error CS0411: The type arguments for method 'C.M(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // M(default, null); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M").WithArguments("C.M(T, T)").WithLocation(6, 9) + ); + } + + [Fact] + public void InvocationOnDefault() + { + string source = @" +class C +{ + static void Main() + { + default.ToString(); + default[0].ToString(); + System.Console.Write(nameof(default)); + throw default; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,16): error CS0023: Operator '.' cannot be applied to operand of type 'default' + // default.ToString(); + Diagnostic(ErrorCode.ERR_BadUnaryOp, ".").WithArguments(".", "default").WithLocation(6, 16), + // (7,9): error CS0021: Cannot apply indexing with [] to an expression of type 'default' + // default[0].ToString(); + Diagnostic(ErrorCode.ERR_BadIndexLHS, "default[0]").WithArguments("default").WithLocation(7, 9), + // (8,37): error CS8081: Expression does not have a name. + // System.Console.Write(nameof(default)); + Diagnostic(ErrorCode.ERR_ExpressionHasNoName, "default").WithLocation(8, 37), + // (9,15): error CS0155: The type caught or thrown must be derived from System.Exception + // throw default; + Diagnostic(ErrorCode.ERR_BadExceptionType, "default").WithLocation(9, 15) + ); + } + + [Fact] + public void Cast() + { + string source = @" +class C +{ + static void Main() + { + int x = (int)default; + System.Console.Write(x); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0"); + } + + [Fact] + public void GenericCast() + { + string source = @" +class C +{ + static void M() + { + const T x = default(T); + const T y = (T)default; + const object z = (T)default; + System.Console.Write($""{x} {y} {z}""); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,15): error CS0283: The type 'T' cannot be declared const + // const T x = default(T); + Diagnostic(ErrorCode.ERR_BadConstType, "T").WithArguments("T").WithLocation(6, 15), + // (7,15): error CS0283: The type 'T' cannot be declared const + // const T y = (T)default; + Diagnostic(ErrorCode.ERR_BadConstType, "T").WithArguments("T").WithLocation(7, 15), + // (8,26): error CS0133: The expression being assigned to 'z' must be constant + // const object z = (T)default; + Diagnostic(ErrorCode.ERR_NotConstantExpression, "(T)default").WithArguments("z").WithLocation(8, 26) + ); + } + + [Fact] + public void UserDefinedStruct() + { + string source = @" +struct S { } +class C +{ + static void M() + { + const S x = default(S); + const S y = (S)default; + const object z = (S)default; + System.Console.Write($""{x} {y} {z}""); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (7,15): error CS0283: The type 'S' cannot be declared const + // const S x = default(S); + Diagnostic(ErrorCode.ERR_BadConstType, "S").WithArguments("S").WithLocation(7, 15), + // (8,15): error CS0283: The type 'S' cannot be declared const + // const S y = (S)default; + Diagnostic(ErrorCode.ERR_BadConstType, "S").WithArguments("S").WithLocation(8, 15), + // (9,26): error CS0133: The expression being assigned to 'z' must be constant + // const object z = (S)default; + Diagnostic(ErrorCode.ERR_NotConstantExpression, "(S)default").WithArguments("z").WithLocation(9, 26) + ); + } + + [Fact] + public void ImplicitlyTypedArray() + { + string source = @" +class C +{ + static void Main() + { + var t = new[] { 1, default }; + System.Console.Write(t[1]); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().ElementAt(1); + Assert.Equal("default", def.ToString()); + Assert.Equal("System.Int32", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Equal("System.Int32", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Equal("0", model.GetConstantValue(def).Value.ToString()); + } + + [Fact] + public void CollectionInitializer() + { + string source = @" +class C +{ + static void Main() + { + var t = new System.Collections.Generic.List { 1, default }; + System.Console.Write($""{t[0]} {t[1]}""); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "1 0"); + } + + [Fact] + public void MiscDefaultErrors() + { + string source = @" +class C +{ + static void Main() + { + switch (default) + { + default: + break; + } + lock (default) + { + } + default(); + + int i = ++default; + var anon = new { Name = default }; + System.TypedReference tr = __makeref(default); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (14,17): error CS1031: Type expected + // default(); + Diagnostic(ErrorCode.ERR_TypeExpected, ")").WithLocation(14, 17), + // (6,17): error CS8119: The switch expression must be a value; found 'default'. + // switch (default) + Diagnostic(ErrorCode.ERR_SwitchExpressionValueExpected, "default").WithArguments("default").WithLocation(6, 17), + // (11,15): error CS0185: 'default' is not a reference type as required by the lock statement + // lock (default) + Diagnostic(ErrorCode.ERR_LockNeedsReference, "default").WithArguments("default").WithLocation(11, 15), + // (16,19): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer + // int i = ++default; + Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "default").WithLocation(16, 19), + // (17,26): error CS0828: Cannot assign 'default' to anonymous type property + // var anon = new { Name = default }; + Diagnostic(ErrorCode.ERR_AnonymousTypePropertyAssignedBadValue, "Name = default").WithArguments("default").WithLocation(17, 26), + // (18,46): error CS1510: A ref or out value must be an assignable variable + // System.TypedReference tr = __makeref(default); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default").WithLocation(18, 46) + ); + } + + [Fact] + public void InChecked() + { + string source = @" +class C +{ + static void Main() + { + int i = checked(default); + int j = checked(default + 4); + System.Console.Write($""{i} {j}""); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0 4"); + } + + [Fact] + public void WithUserDefinedPlusOperator() + { + string source = @" +struct S +{ + int field; + static void Main() + { + S s = new S(40) + default; + s += new S(2); + s += default; + System.Console.Write(s); + } + S(int i) { field = i; } + public static S operator +(S left, S right) => new S(left.field + right.field); + public override string ToString() => field.ToString(); +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "42"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var first = nodes.OfType().ElementAt(1); + Assert.Equal("new S(40) + default", first.Parent.ToString()); + Assert.Equal("S", model.GetTypeInfo(first).Type.ToTestDisplayString()); + + var second = nodes.OfType().ElementAt(3); + Assert.Equal("s += default", second.Parent.ToString()); + Assert.Equal("S", model.GetTypeInfo(second).Type.ToTestDisplayString()); + } + + [Fact] + public void WithUserDefinedEqualityOperator() + { + string source = @" +struct S +{ + static void Main() + { + if (new S() == default) + { + System.Console.Write(""branch reached.""); + } + } + public static bool operator ==(S left, S right) { System.Console.Write(""operator reached. ""); return true; } + public static bool operator !=(S left, S right) => false; + public override bool Equals(object o) => throw null; + public override int GetHashCode() => throw null; +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "operator reached. branch reached."); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var first = nodes.OfType().ElementAt(0); + Assert.Equal("default", first.ToString()); + Assert.Equal("S", model.GetTypeInfo(first).Type.ToTestDisplayString()); + } + + [Fact] + public void RefTypeAndValue() + { + string source = @" +class C +{ + static void Main() + { + System.Console.Write(1); + var t = __reftype(default); + System.Console.Write(2); + try + { + int rv = __refvalue(default, int); + } + catch (System.InvalidCastException) + { + System.Console.Write($""3: {t == null}""); + } + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "123: True"); + } + + [Fact] + public void InCompoundAssignmentAndExceptionFilter() + { + string source = @" +class C +{ + static void Main() + { + try + { + int i = 2; + i += default; + bool b = true; + b &= default; + System.Console.Write($""{true | default} {i} {b}""); + throw new System.Exception(); + } + catch (System.Exception) when (default) + { + System.Console.Write(""catch""); + } + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (15,40): warning CS7095: Filter expression is a constant, consider removing the filter + // catch (System.Exception) when (default) + Diagnostic(ErrorCode.WRN_FilterIsConstant, "default").WithLocation(15, 40), + // (17,13): warning CS0162: Unreachable code detected + // System.Console.Write("catch"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(17, 13) + ); + //CompileAndVerify(comp, expectedOutput: "True 2 False"); // PEVerify failed with Branch out of the method. Follow-up issue: https://github.com/dotnet/roslyn/issues/18678 + } + + [Fact] + public void PEVerifyErrorWithFalse() + { + string source = @" +class C +{ + static void Main() + { + try + { + throw new System.Exception(); + } + catch (System.Exception) when (false) + { + System.Console.Write(""catch""); + } + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (10,40): warning CS7095: Filter expression is a constant, consider removing the filter + // catch (System.Exception) when (false) + Diagnostic(ErrorCode.WRN_FilterIsConstant, "false").WithLocation(10, 40), + // (12,13): warning CS0162: Unreachable code detected + // System.Console.Write("catch"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(12, 13) + ); + //CompileAndVerify(comp); // PEVerify failed with Branch out of the method. Follow-up issue: https://github.com/dotnet/roslyn/issues/18678 + } + + [Fact] + public void NegationUnaryOperatorOnDefault() + { + string source = @" +class C +{ + static void Main() + { + if (!default) + { + System.Console.WriteLine(""reached""); + } + } +}"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "reached"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType().ElementAt(0); + Assert.Equal("default", def.ToString()); + Assert.Equal("System.Boolean", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Equal("System.Boolean", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void NegationUnaryOperatorOnTypelessExpressions() + { + string source = @" +class C +{ + static void Main() + { + if (!Main || !null) + { + } + } +}"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,13): error CS0023: Operator '!' cannot be applied to operand of type 'method group' + // if (!Main || !null) + Diagnostic(ErrorCode.ERR_BadUnaryOp, "!Main").WithArguments("!", "method group"), + // (6,22): error CS0023: Operator '!' cannot be applied to operand of type '' + // if (!Main || !null) + Diagnostic(ErrorCode.ERR_BadUnaryOp, "!null").WithArguments("!", "").WithLocation(6, 22) + ); + } + + [Fact] + public void ConditionalOnDefault() + { + string source = @" +class C +{ + static void Main() + { + if (default) + { + System.Console.Write(""if""); + } + + while (default) + { + System.Console.Write(""while""); + } + + for (int i = 0; default; i++) + { + System.Console.Write(""for""); + } + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (8,13): warning CS0162: Unreachable code detected + // System.Console.Write("if"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(8, 13), + // (13,13): warning CS0162: Unreachable code detected + // System.Console.Write("while"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(13, 13), + // (18,13): warning CS0162: Unreachable code detected + // System.Console.Write("for"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(18, 13) + ); + } + + [Fact] + public void ConditionalOnDefaultIsFalse() + { + string source = @" +class C +{ + static void Main() + { + if (default == false) + { + System.Console.Write(""reached""); + } + if (default == true) + { + System.Console.Write(""NEVER""); + } + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (12,13): warning CS0162: Unreachable code detected + // System.Console.Write("NEVER"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(12, 13) + ); + CompileAndVerify(comp, expectedOutput: "reached"); + } + + [Fact] + public void InFixed() + { + string source = @" +class C +{ + static unsafe void Main() + { + fixed (byte* p = default) + { + } + fixed (byte* p = &default) + { + } + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + comp.VerifyDiagnostics( + // (6,26): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + // fixed (byte* p = default) + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "default"), + // (9,27): error CS0211: Cannot take the address of the given expression + // fixed (byte* p = &default) + Diagnostic(ErrorCode.ERR_InvalidAddrOp, "default").WithLocation(9, 27) + ); + } + + [Fact] + public void Dereference() + { + string source = @" +class C +{ + static void M() + { + var p = *default; + var q = default->F; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,17): error CS0193: The * or -> operator must be applied to a pointer + // var p = *default; + Diagnostic(ErrorCode.ERR_PtrExpected, "*default").WithLocation(6, 17), + // (7,17): error CS0193: The * or -> operator must be applied to a pointer + // var q = default->F; + Diagnostic(ErrorCode.ERR_PtrExpected, "default->F").WithLocation(7, 17) + ); + } + + [Fact] + public void FailedImplicitlyTypedArray() + { + string source = @" +class C +{ + static void Main() + { + var t = new[] { default, default }; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,17): error CS0826: No best type found for implicitly-typed array + // var t = new[] { default, default }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { default, default }").WithLocation(6, 17) + ); + } + + [Fact] + public void ArrayConstruction() + { + string source = @" +class C +{ + static void Main() + { + var t = new object[default]; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + + [Fact] + public void Tuple() + { + string source = @" +class C +{ + static void Main() + { + (int, int) t = (1, default); + System.Console.Write(t.Item2); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe, + references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0"); + } + + [Fact] + public void TypeInferenceSucceeds() + { + string source = @" +class C +{ + static void Main() + { + M(default, 1); + } + static void M(T x, T y) { System.Console.Write(x); } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0"); + } + + [Fact] + public void ArrayTypeInferredFromParams() + { + string source = @" +class C +{ + static void Main() + { + M(default); + M(null); + } + static void M(params object[] x) { } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().ElementAt(0); + Assert.Equal("default", def.ToString()); + Assert.Equal("System.Object[]", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Equal("System.Object[]", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Null(model.GetDeclaredSymbol(def)); + + var nullSyntax = nodes.OfType().ElementAt(1); + Assert.Equal("null", nullSyntax.ToString()); + Assert.Equal("System.Object[]", model.GetTypeInfo(nullSyntax).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void ParamsAmbiguity() + { + string source = @" +class C +{ + static void Main() + { + M(default); + } + static void M(params object[] x) { } + static void M(params int[] x) { } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyEmitDiagnostics( + // (6,9): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(params object[])' and 'C.M(params int[])' + // M(default); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(params object[])", "C.M(params int[])").WithLocation(6, 9) + ); + } + + [Fact] + public void ParamsAmbiguity2() + { + string source = @" +class C +{ + static void Main() + { + M(default); + } + static void M(params object[] x) { } + static void M(int x) { } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyEmitDiagnostics( + // (6,9): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(params object[])' and 'C.M(int)' + // M(default); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(params object[])", "C.M(int)").WithLocation(6, 9) + ); + } + + [Fact] + public void ParamsAmbiguity3() + { + string source = @" +struct S +{ + static void Main() + { + object o = null; + S s = default; + M(o, default); + M(default, o); + M(s, default); + M(default, s); + } + static void M(T x, params T[] y) { } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var first = nodes.OfType().ElementAt(2); + Assert.Equal("(o, default)", first.Parent.Parent.ToString()); + Assert.Equal("System.Object[]", model.GetTypeInfo(first).Type.ToTestDisplayString()); + + var second = nodes.OfType().ElementAt(3); + Assert.Equal("(default, o)", second.Parent.Parent.ToString()); + Assert.Equal("System.Object", model.GetTypeInfo(second).Type.ToTestDisplayString()); + + var third = nodes.OfType().ElementAt(4); + Assert.Equal("(s, default)", third.Parent.Parent.ToString()); + Assert.Equal("S[]", model.GetTypeInfo(third).Type.ToTestDisplayString()); + + var fourth = nodes.OfType().ElementAt(5); + Assert.Equal("(default, s)", fourth.Parent.Parent.ToString()); + Assert.Equal("S", model.GetTypeInfo(fourth).Type.ToTestDisplayString()); + } + + [Fact] + public void DefaultIdentifier() + { + string source = @" +class C +{ + static void Main() + { + int @default = 2; + int x = default; + System.Console.Write($""{x} {@default}""); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0 2"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().ElementAt(1); + Assert.Equal("default", def.ToString()); + Assert.Equal("System.Int32", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Null(model.GetDeclaredSymbol(def)); + } + + [Fact] + public void TestSpeculativeModel() + { + string source = @" +class C +{ + static void Main() + { + int i = 2; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var digit = tree.GetCompilationUnitRoot().FindToken(source.IndexOf('2')); + var expressionSyntax = SyntaxFactory.ParseExpression("default"); + var typeInfo = model.GetSpeculativeTypeInfo(digit.SpanStart, expressionSyntax, SpeculativeBindingOption.BindAsExpression); + Assert.Null(typeInfo.Type); + var symbol = model.GetSpeculativeSymbolInfo(digit.SpanStart, expressionSyntax, SpeculativeBindingOption.BindAsExpression); + Assert.True(symbol.IsEmpty); + } + + [Fact] + public void Return() + { + string source = @" +class C +{ + static int M() + { + return default; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + + [Fact] + public void DefaultInEnum() + { + string source = @" +enum E +{ + DefaultEntry = default, + OneEntry = default + 1 +} +class C +{ + static void Main() + { + System.Console.Write($""{(int)E.DefaultEntry} {(int)E.OneEntry}""); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0 1"); + } + + [Fact] + public void DefaultInTypedEnum() + { + string source = @" +enum E : byte +{ + DefaultEntry = default, + OneEntry = default + 1 +} +class C +{ + static void Main() + { + System.Console.Write($""{(byte)E.DefaultEntry} {(byte)E.OneEntry}""); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0 1"); + } + + [Fact] + public void YieldReturn() + { + string source = @" +using System.Collections; +using System.Collections.Generic; +class C +{ + static IEnumerable M() + { + yield return default; + } + static IEnumerable M2() + { + yield return default; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ReturnNullableType() + { + string source = @" +class C +{ + static int? M() + { + return default; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ConstAndProperty() + { + string source = @" +class C +{ + const int x = default; + static int P { get { return default; } } + static void Main() + { + System.Console.Write($""{x}-{P}""); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0-0"); + } + + [Fact] + public void InvocationOnDynamic() + { + string source = @" +class C +{ + static void M1() + { + dynamic d = null; + d.M2(default); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (7,14): error CS9000: Cannot use a default literal as an argument to a dynamically dispatched operation. + // d.M2(default); + Diagnostic(ErrorCode.ERR_BadDynamicMethodArgDefaultLiteral, "default").WithLocation(7, 14) + ); + } + + [Fact] + public void DynamicInvocation() + { + string source = @" +class C +{ + static void Main() + { + F(default); + } + static void F(dynamic x) + { + System.Console.Write(x == null); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, references: new[] { SystemCoreRef, CSharpRef }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "True"); + } + + [Fact] + public void DefaultEqualsDefault() + { + string source = @" +class C +{ + static void Main() + { + System.Console.Write($""{default == default} {default != default}""); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,33): error CS0034: Operator '==' is ambiguous on operands of type 'default' and 'default' + // System.Console.Write($"{default == default} {default != default}"); + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "default == default").WithArguments("==", "default", "default").WithLocation(6, 33), + // (6,54): error CS0034: Operator '!=' is ambiguous on operands of type 'default' and 'default' + // System.Console.Write($"{default == default} {default != default}"); + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "default != default").WithArguments("!=", "default", "default").WithLocation(6, 54) + ); + } + + [Fact] + public void NormalInitializerType_Default() + { + var text = @" +class Program +{ + unsafe static void Main() + { + fixed (int* p = default) + { + } + } +} +"; + // Confusing, but matches Dev10. + CreateStandardCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular7_1) + .VerifyDiagnostics( + // (6,25): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + // fixed (int* p = default) + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "default").WithLocation(6, 25) + ); + } + + [Fact] + public void TestErrorDefaultLiteralCollection() + { + var text = @" +class C +{ + static void Main() + { + foreach (int x in default) { } + foreach (int x in null) { } + } +}"; + + var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,27): error CS9001: Use of default literal is not valid in this context + // foreach (int x in default) { } + Diagnostic(ErrorCode.ERR_DefaultLiteralNotValid, "default").WithLocation(6, 27), + // (7,27): error CS0186: Use of null is not valid in this context + // foreach (int x in null) { } + Diagnostic(ErrorCode.ERR_NullNotValid, "null").WithLocation(7, 27) + ); + } + + [Fact] + public void QueryOnDefault() + { + string source = +@"using System.Linq; +static class C +{ + static void Main() + { + var q = from x in default select x; + var p = from x in new int[] { 1 } select default; + } +} +"; + var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.Regular7_1, references: new[] { SystemCoreRef }); + compilation.VerifyDiagnostics( + // (6,35): error CS9001: Use of default literal is not valid in this context + // var q = from x in default select x; + Diagnostic(ErrorCode.ERR_DefaultLiteralNotValid, "select x").WithLocation(6, 35), + // (7,43): error CS1942: The type of the expression in the select clause is incorrect. Type inference failed in the call to 'Select'. + // var p = from x in new int[] { 1 } select default; + Diagnostic(ErrorCode.ERR_QueryTypeInferenceFailed, "select").WithArguments("select", "Select").WithLocation(7, 43) + ); + } + + [Fact] + public void DefaultInConditionalExpression() + { + string source = +@"static class C +{ + static void Main() + { + var x = default ? 4 : 5; + System.Console.Write(x); + } +} +"; + var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "5"); + } + + [Fact] + public void AlwaysNonNull() + { + string source = +@"static class C +{ + static void Main() + { + System.Console.Write((int?)1 == default); + System.Console.Write(default == (int?)1); + } +} +"; + var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.Regular7_1); + compilation.VerifyDiagnostics( + // (5,30): warning CS0472: The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?' + // System.Console.Write((int?)1 == default); + Diagnostic(ErrorCode.WRN_NubExprIsConstBool, "(int?)1 == default").WithArguments("false", "int", "int?").WithLocation(5, 30), + // (6,30): warning CS0472: The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?' + // System.Console.Write(default == (int?)1); + Diagnostic(ErrorCode.WRN_NubExprIsConstBool, "default == (int?)1").WithArguments("false", "int", "int?").WithLocation(6, 30) + ); + } + + [Fact] + public void ThrowDefault() + { + var text = @" +class C +{ + static void Main() + { + throw default; + } +}"; + + var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,15): error CS0155: The type caught or thrown must be derived from System.Exception + // throw default; + Diagnostic(ErrorCode.ERR_BadExceptionType, "default").WithLocation(6, 15) + ); + } + + [Fact] + public void DefaultInAsOperator() + { + var text = @" +class C +{ + static void M() where TClass : class + { + System.Console.Write(default as long); + System.Console.Write(default as T); + System.Console.Write(default as TClass); + } +}"; + + var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugDll); + comp.VerifyDiagnostics( + // (6,30): error CS0077: The as operator must be used with a reference type or nullable type ('long' is a non-nullable value type) + // System.Console.Write(default as long); + Diagnostic(ErrorCode.ERR_AsMustHaveReferenceType, "default as long").WithArguments("long").WithLocation(6, 30), + // (7,30): error CS0413: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint + // System.Console.Write(default as T); + Diagnostic(ErrorCode.ERR_AsWithTypeVar, "default as T").WithArguments("T").WithLocation(7, 30), + // (8,30): warning CS0458: The result of the expression is always 'null' of type 'TClass' + // System.Console.Write(default as TClass); + Diagnostic(ErrorCode.WRN_AlwaysNull, "default as TClass").WithArguments("TClass").WithLocation(8, 30) + ); + } + + [Fact] + public void DefaultInAsOperatorWithReferenceType() + { + var text = @" +class C +{ + static void Main() + { + System.Console.Write($""{default as C == null} {default as string == null}""); + } +}"; + var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,33): warning CS0458: The result of the expression is always 'null' of type 'C' + // System.Console.Write($"{default as C == null} {default as string == null}"); + Diagnostic(ErrorCode.WRN_AlwaysNull, "default as C").WithArguments("C").WithLocation(6, 33), + // (6,56): warning CS0458: The result of the expression is always 'null' of type 'string' + // System.Console.Write($"{default as C == null} {default as string == null}"); + Diagnostic(ErrorCode.WRN_AlwaysNull, "default as string").WithArguments("string").WithLocation(6, 56) + ); + CompileAndVerify(comp, expectedOutput: "True True"); + } + + [Fact] + public void DefaultInputToTypeTest() + { + var text = @" +static class C +{ + static void M() + { + System.Console.Write(default is C); + } +}"; + + var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugDll); + comp.VerifyDiagnostics( + // (6,30): error CS0023: Operator 'is' cannot be applied to operand of type 'default' + // System.Console.Write(default is C); + Diagnostic(ErrorCode.ERR_BadUnaryOp, "default is C").WithArguments("is", "default").WithLocation(6, 30) + ); + } + + [Fact] + public void DefaultInputToConstantPattern() + { + var text = @" +class C +{ + static void M() + { + System.Console.Write(default is long); + System.Console.Write(default is string); + System.Console.Write(default is default); + System.Console.Write(default is T); + } +}"; + + var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugDll); + comp.VerifyDiagnostics( + // (6,30): error CS0023: Operator 'is' cannot be applied to operand of type 'default' + // System.Console.Write(default is long); + Diagnostic(ErrorCode.ERR_BadUnaryOp, "default is long").WithArguments("is", "default").WithLocation(6, 30), + // (7,30): error CS0023: Operator 'is' cannot be applied to operand of type 'default' + // System.Console.Write(default is string); + Diagnostic(ErrorCode.ERR_BadUnaryOp, "default is string").WithArguments("is", "default").WithLocation(7, 30), + // (8,30): error CS0023: Operator 'is' cannot be applied to operand of type 'default' + // System.Console.Write(default is default); + Diagnostic(ErrorCode.ERR_BadUnaryOp, "default is default").WithArguments("is", "default").WithLocation(8, 30), + // (8,41): error CS0150: A constant value is expected + // System.Console.Write(default is default); + Diagnostic(ErrorCode.ERR_ConstantExpected, "default").WithLocation(8, 41), + // (9,30): error CS0023: Operator 'is' cannot be applied to operand of type 'default' + // System.Console.Write(default is T); + Diagnostic(ErrorCode.ERR_BadUnaryOp, "default is T").WithArguments("is", "default").WithLocation(9, 30) + ); + } + + [Fact] + public void DefaultInConstantPattern() + { + var text = @" +class C +{ + static void Main() + { + string hello = ""hello""; + string nullString = null; + int two = 2; + int zero = 0; + System.Console.Write($""{hello is default} {nullString is default} {two is default} {zero is default}""); + } +}"; + + var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "False True False True"); + } + + [Fact] + public void TypeVarCanBeDefault() + { + var source = +@"interface I { } +class A { } +class B + where T2 : class + where T3 : struct + where T4 : new() + where T5 : I + where T6 : A + where T7 : T1 +{ + static void M() + { + T1 t1 = default; + T2 t2 = default; + T3 t3 = default; + T4 t4 = default; + T5 t5 = default; + T6 t6 = default; + T7 t7 = default; + System.Console.Write($""{t1} {t2} {t3} {t4} {t5} {t6} {t7}""); + } + static T1 F1() { return default; } + static T2 F2() { return default; } + static T3 F3() { return default; } + static T4 F4() { return default; } + static T5 F5() { return default; } + static T6 F6() { return default; } + static T7 F7() { return default; } +}"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ExprTreeConvertedNullOnLHS() + { + var text = +@"using System; +using System.Linq.Expressions; + +class Program +{ + Expression> testExpr = () => default ?? ""hello""; +}"; + + var comp = CreateCompilationWithMscorlibAndSystemCore(text, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,47): error CS0845: An expression tree lambda may not contain a coalescing operator with a null or default literal left-hand side + // Expression> testExpr = () => default ?? "hello"; + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsBadCoalesce, "default").WithLocation(6, 47) + ); + } + + [Fact] + public void NullableAndDefault() + { + var text = +@"class Program +{ + static void Main() + { + int? x = default; + System.Console.Write(x.HasValue); + } +}"; + + var comp = CreateCompilationWithMscorlibAndSystemCore(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "False"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single(); + Assert.Equal("System.Int32?", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Equal("System.Int32?", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.False(model.GetConstantValue(def).HasValue); + Assert.True(model.GetConversion(def).IsNullLiteral); + } + + [Fact] + public void IndexingIntoArray() + { + string source = @" +class C +{ + static void Main() + { + int[] x = { 1, 2 }; + System.Console.Write(x[default]); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "1"); + } + + [Fact] + public void Lambda() + { + string source = @" +class C +{ + static void Main() + { + System.Console.Write(M()()); + } + static System.Func M() + { + return () => default; + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0"); + } + + [Fact] + public void V6SwitchWarns() + { + string source = @" +class C +{ + static void Main() + { + M(0); + } + static void M(int x) + { + switch (x) + { + case default: + System.Console.Write(""default""); + break; + default: + break; + } + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (12,18): warning CS9002: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. + // case default: + Diagnostic(ErrorCode.WRN_DefaultInSwitch, "default").WithLocation(12, 18) + ); + CompileAndVerify(comp, expectedOutput: "default"); + } + + [Fact] + public void V7SwitchWarns() + { + string source = @" +class C +{ + static void Main() + { + M(null); + } + static void M(object x) + { + switch (x) + { + case default: + System.Console.Write(""default""); + break; + default: + break; + } + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (12,18): warning CS9002: Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. + // case default: + Diagnostic(ErrorCode.WRN_DefaultInSwitch, "default").WithLocation(12, 18) + ); + CompileAndVerify(comp, expectedOutput: "default"); + } + + [Fact] + public void V6SwitchWarningWorkaround() + { + string source = @" +class C +{ + static void Main() + { + M(0); + } + static void M(int x) + { + switch (x) + { + case (default): + System.Console.Write(""default""); + break; + default: + break; + } + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "default"); + } + + [Fact] + public void V7SwitchWarningWorkaround() + { + string source = @" +class C +{ + static void Main() + { + M(null); + } + static void M(object x) + { + switch (x) + { + case (default): + System.Console.Write(""default""); + break; + default: + break; + } + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "default"); + } + + [Fact] + public void BinaryOperator() + { + string source = @" +class C +{ + static void Main() + { + int x = 0; + if (x == default) + { + System.Console.Write(""0""); + } + if (default == x) + { + System.Console.Write(""1""); + } + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "01"); + } + + [Fact] + public void OptionalParameter() + { + string source = @" +class C +{ + static void Main() + { + M(); + } + static void M(int x = default) + { + System.Console.Write(x); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0"); + } + + [Fact] + public void OptionalCancellationTokenParameter() + { + string source = @" +class C +{ + static void Main() + { + M(); + } + static void M(System.Threading.CancellationToken x = default) + { + System.Console.Write(""ran""); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "ran"); + } + + [Fact] + public void ArraySize() + { + string source = @" +class C +{ + static void Main() + { + var a = new int[default]; + System.Console.Write(a.Length); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0"); + } + + [Fact] + public void TernaryOperator() + { + string source = @" +class C +{ + static void Main() + { + bool flag = true; + var x = flag ? default : 1; + System.Console.Write($""{x} {x.GetType().ToString()}""); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0 System.Int32"); + } + + [Fact] + public void RefTernaryOperator() + { + string source = @" +class C +{ + static void Main() + { + bool flag = true; + var x = flag ? default : ""hello""; + System.Console.Write(x == null ? ""null"" : ""bad""); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "null"); + } + + [Fact] + public void ExplicitCast() + { + string source = @" +class C +{ + static void Main() + { + int x = (short)default; + System.Console.Write(x); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().Single(); + Assert.Equal("System.Int16", model.GetTypeInfo(def).Type.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Null(model.GetDeclaredSymbol(def)); + Assert.Equal("System.Int16", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Equal((short)0, model.GetConstantValue(def).Value); + Assert.True(model.GetConversion(def).IsIdentity); + + var conversionSyntax = nodes.OfType().Single(); + var conversionTypeInfo = model.GetTypeInfo(conversionSyntax); + Assert.Equal("System.Int16", conversionTypeInfo.Type.ToTestDisplayString()); + Assert.Equal("System.Int32", conversionTypeInfo.ConvertedType.ToTestDisplayString()); + Assert.Equal((short)0, model.GetConstantValue(conversionSyntax).Value); + Conversion conversion = model.GetConversion(conversionSyntax); + Assert.True(conversion.IsNumeric); + Assert.True(conversion.IsImplicit); + } + + [Fact] + public void NotAType() + { + string source = @" +class C +{ + static void Main() + { + default(System).ToString(); + } +} +"; + + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,17): error CS0118: 'System' is a namespace but is used like a type + // default(System).ToString(); + Diagnostic(ErrorCode.ERR_BadSKknown, "System").WithArguments("System", "namespace", "type").WithLocation(6, 17) + ); + } + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 617bf258d7913..f5158cbe21688 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -1853,7 +1853,7 @@ S MakeS() No, ObjectCreationExpression 'new S()' is not a non-moveable variable No, Conversion 'default(S).i' is not a non-moveable variable No, FieldAccess 'default(S).i' is not a non-moveable variable -No, DefaultOperator 'default(S)' is not a non-moveable variable +No, DefaultExpression 'default(S)' is not a non-moveable variable No, Conversion 'MakeS().i' is not a non-moveable variable No, FieldAccess 'MakeS().i' is not a non-moveable variable No, Call 'MakeS()' is not a non-moveable variable diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs index 5409afe9cfbc2..8892410f5f472 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs @@ -530,7 +530,7 @@ enum E { zero, one } // sbyte? nullable = null; var v1 = (mainStats[0] as LocalDeclarationStatementSyntax).Declaration.Variables; - ConversionTestHelper(model, v1[0].Initializer.Value, ConversionKind.NullLiteral, ConversionKind.NoConversion); + ConversionTestHelper(model, v1[0].Initializer.Value, ConversionKind.DefaultOrNullLiteral, ConversionKind.NoConversion); // uint? nullable01 = 100; var v2 = (mainStats[1] as LocalDeclarationStatementSyntax).Declaration.Variables; ConversionTestHelper(model, v2[0].Initializer.Value, ConversionKind.ImplicitNullable, ConversionKind.ExplicitNullable); @@ -681,7 +681,7 @@ private void ValidateConversion(Conversion conv, ConversionKind kind) Assert.False(conv.IsExplicit); Assert.True(conv.IsNullable); break; - case ConversionKind.NullLiteral: + case ConversionKind.DefaultOrNullLiteral: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs index 15abb2c6810b9..d4d8756f7777b 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs @@ -3533,6 +3533,18 @@ void M() EOF(); } + [Fact] + public void TestTargetTypedDefaultWithCSharp7_1() + { + var text = "default"; + var expr = this.ParseExpression(text, TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)); + + Assert.NotNull(expr); + Assert.Equal(SyntaxKind.DefaultLiteralExpression, expr.Kind()); + Assert.Equal(text, expr.ToString()); + Assert.Equal(0, expr.Errors().Length); + } + [Fact, WorkItem(17683, "https://github.com/dotnet/roslyn/issues/17683")] public void Bug17683a() { @@ -3656,4 +3668,4 @@ public void Bug17683b() EOF(); } } -} \ No newline at end of file +} diff --git a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs index 17e64e05bcdd6..0ec664f2163fa 100644 --- a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs +++ b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs @@ -14,11 +14,12 @@ public static class TestOptions public static readonly CSharpParseOptions Script = new CSharpParseOptions(kind: SourceCodeKind.Script, documentationMode: DocumentationMode.None); public static readonly CSharpParseOptions Regular = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None); public static readonly CSharpParseOptions Regular6 = Regular.WithLanguageVersion(LanguageVersion.CSharp6); + public static readonly CSharpParseOptions Regular7_1 = Regular.WithLanguageVersion(LanguageVersion.CSharp7_1); public static readonly CSharpParseOptions RegularWithDocumentationComments = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.Diagnose); private static readonly SmallDictionary s_experimentalFeatures = new SmallDictionary { }; public static readonly CSharpParseOptions ExperimentalParseOptions = - new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None, languageVersion: LanguageVersion.CSharp7).WithFeatures(s_experimentalFeatures); + new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None, languageVersion: LanguageVersion.Latest).WithFeatures(s_experimentalFeatures); // Enable pattern-switch translation even for switches that use no new syntax. This is used // to help ensure compatibility of the semantics of the new switch binder with the old switch diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs index 8e31331d4f703..f02eea5112eca 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs @@ -118,7 +118,7 @@ private static BoundExpression GetCustomTypeInfoPayloadId(SyntaxNode syntax, Met { if (!hasCustomTypeInfoPayload) { - return new BoundDefaultOperator(syntax, guidConstructor.ContainingType); + return new BoundDefaultExpression(syntax, guidConstructor.ContainingType); } var value = ConstantValue.Create(CustomTypeInfo.PayloadTypeId.ToString()); diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs index aa3af0b960133..c0f4f0fc2b3d0 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs @@ -194,9 +194,11 @@ private static ExpressionSyntax ParseDebuggerExpression(string text, bool consum return expression.MakeDebuggerExpression(source); } + static readonly CSharpParseOptions s_CSharpParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest); + private static InternalSyntax.ExpressionSyntax ParseDebuggerExpressionInternal(SourceText source, bool consumeFullText) { - using (var lexer = new InternalSyntax.Lexer(source, CSharpParseOptions.Default, allowPreprocessorDirectives: false)) + using (var lexer = new InternalSyntax.Lexer(source, s_CSharpParseOptions, allowPreprocessorDirectives: false)) { using (var parser = new InternalSyntax.LanguageParser(lexer, oldTree: null, changes: null, lexerMode: InternalSyntax.LexerMode.DebuggerSyntax)) { @@ -210,7 +212,7 @@ private static InternalSyntax.ExpressionSyntax ParseDebuggerExpressionInternal(S private static StatementSyntax ParseDebuggerStatement(string text) { var source = SourceText.From(text); - using (var lexer = new InternalSyntax.Lexer(source, CSharpParseOptions.Default)) + using (var lexer = new InternalSyntax.Lexer(source, s_CSharpParseOptions)) { using (var parser = new InternalSyntax.LanguageParser(lexer, oldTree: null, changes: null, lexerMode: InternalSyntax.LexerMode.DebuggerSyntax)) { diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 7245057008d74..a8cb18376129a 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -3462,7 +3462,7 @@ static void M() string error; var testData = new CompilationTestData(); context.CompileExpression("F(() => null ?? new object())", out error, testData); - Assert.Equal(error, "error CS0845: An expression tree lambda may not contain a coalescing operator with a null literal left-hand side"); + Assert.Equal(error, "error CS0845: An expression tree lambda may not contain a coalescing operator with a null or default literal left-hand side"); }); } @@ -6205,5 +6205,109 @@ .locals init (int V_0, }"); }); } + + [Fact] + public void AssignDefaultToLocal() + { + var source = @" +class C +{ + void Test() + { + int a = 1; + } +} +"; + var comp = CreateStandardCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.Regular); + WithRuntimeInstance(comp, runtime => + { + var context = CreateMethodContext(runtime, methodName: "C.Test"); + + ResultProperties resultProperties; + string error; + var testData = new CompilationTestData(); + ImmutableArray missingAssemblyIdentities; + context.CompileAssignment("a", "default", NoAliases, DebuggerDiagnosticFormatter.Instance, out resultProperties, out error, out missingAssemblyIdentities, EnsureEnglishUICulture.PreferredOrNull, testData); + Assert.Null(error); + Assert.Empty(missingAssemblyIdentities); + + Assert.Equal(DkmClrCompilationResultFlags.PotentialSideEffect, resultProperties.Flags); + Assert.Equal(default(DkmEvaluationResultCategory), resultProperties.Category); // Not Data + Assert.Equal(default(DkmEvaluationResultAccessType), resultProperties.AccessType); // Not Public + Assert.Equal(default(DkmEvaluationResultStorageType), resultProperties.StorageType); + Assert.Equal(default(DkmEvaluationResultTypeModifierFlags), resultProperties.ModifierFlags); // Not Virtual + testData.GetMethodData("<>x.<>m0").VerifyIL(@" +{ + // Code size 3 (0x3) + .maxstack 1 + .locals init (int V_0) //a + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ret +}"); + + testData = new CompilationTestData(); + context.CompileExpression("a = default;", DkmEvaluationFlags.None, ImmutableArray.Empty, out error, testData); + Assert.Null(error); + testData.GetMethodData("<>x.<>m0").VerifyIL(@" +{ + // Code size 4 (0x4) + .maxstack 2 + .locals init (int V_0) //a + IL_0000: ldc.i4.0 + IL_0001: dup + IL_0002: stloc.0 + IL_0003: ret +}"); + testData = new CompilationTestData(); + context.CompileExpression("int b = default;", DkmEvaluationFlags.None, ImmutableArray.Empty, out error, testData); + Assert.Null(error); + testData.GetMethodData("<>x.<>m0").VerifyIL(@" +{ + // Code size 43 (0x2b) + .maxstack 4 + .locals init (int V_0, //a + System.Guid V_1) + IL_0000: ldtoken ""int"" + IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_000a: ldstr ""b"" + IL_000f: ldloca.s V_1 + IL_0011: initobj ""System.Guid"" + IL_0017: ldloc.1 + IL_0018: ldnull + IL_0019: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])"" + IL_001e: ldstr ""b"" + IL_0023: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" + IL_0028: ldc.i4.0 + IL_0029: stind.i4 + IL_002a: ret +}"); + + testData = new CompilationTestData(); + context.CompileExpression("default", DkmEvaluationFlags.None, ImmutableArray.Empty, out error, testData); + Assert.Null(error); + testData.GetMethodData("<>x.<>m0").VerifyIL(@" +{ + // Code size 2 (0x2) + .maxstack 1 + .locals init (int V_0) //a + IL_0000: ldnull + IL_0001: ret +}"); + Assert.Equal(SpecialType.System_Object, testData.GetMethodData("<>x.<>m0").Method.ReturnType.SpecialType); + + testData = new CompilationTestData(); + context.CompileExpression("null", DkmEvaluationFlags.None, ImmutableArray.Empty, out error, testData); + Assert.Null(error); + testData.GetMethodData("<>x.<>m0").VerifyIL(@" +{ + // Code size 2 (0x2) + .maxstack 1 + .locals init (int V_0) //a + IL_0000: ldnull + IL_0001: ret +}"); + }); + } } } diff --git a/src/Test/Utilities/Portable/TestResource.resx b/src/Test/Utilities/Portable/TestResource.resx index 83733946b5bee..efb2394f0cef6 100644 --- a/src/Test/Utilities/Portable/TestResource.resx +++ b/src/Test/Utilities/Portable/TestResource.resx @@ -181,7 +181,8 @@ namespace My private const int global = int.MinValue - 1; static A() - { + { + int x = default; } [method: Obsolete] diff --git a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs index 3c0c17efd0fc3..e3ac493c97775 100644 --- a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs +++ b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs @@ -19,5 +19,6 @@ public enum CompilerFeature RefLocalsReturns, OutVar, Patterns, + DefaultLiteral, } }