From 4dc80f174d8ea11b802b21a24a4249b823246921 Mon Sep 17 00:00:00 2001 From: jcouv Date: Mon, 9 Jan 2017 00:36:18 -0800 Subject: [PATCH 1/6] Test for semantic model, constant value and more tests for default --- .../Portable/Binder/Binder_Conversions.cs | 4 + .../Portable/Binder/Binder_Expressions.cs | 5 +- .../Portable/Binder/Binder_Invocation.cs | 5 + .../Portable/Binder/Binder_Operators.cs | 2 - .../CSharp/Portable/Binder/Binder_Query.cs | 4 + .../Portable/Binder/Binder_Statements.cs | 5 +- .../Semantics/Conversions/ConversionsBase.cs | 2 +- .../Portable/CSharpResources.Designer.cs | 11 +- .../CSharp/Portable/CSharpResources.resx | 5 +- .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../DiagnosticsPass_ExpressionTrees.cs | 2 +- .../Semantics/TargetTypedDefaultTests.cs | 323 ++++++++++++++++-- src/Compilers/Core/Portable/ConstantValue.cs | 10 + .../Core/Portable/ConstantValueSpecialized.cs | 1 + .../ExpressionCompilerTests.cs | 2 +- 15 files changed, 351 insertions(+), 32 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 0015782f9ead8..454a690bbb63c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -887,6 +887,10 @@ public ConstantValue FoldConstantConversion( case ConversionKind.NullLiteral: return sourceConstantValue; + case ConversionKind.DefaultLiteral: + Debug.Assert(sourceConstantValue.IsDefaultLiteral); + return destination.GetDefaultValue(); + case ConversionKind.ImplicitConstant: return FoldConstantNumericConversion(syntax, sourceConstantValue, destination, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 6624f416d5149..972800d4fee55 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -553,7 +553,7 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic return BindDefaultExpression((DefaultExpressionSyntax)node, diagnostics); case SyntaxKind.DefaultLiteral: - return new BoundDefaultOperator((DefaultLiteralSyntax)node); + return new BoundDefaultOperator((DefaultLiteralSyntax)node, ConstantValue.DefaultLiteral, type: null); case SyntaxKind.TypeOfExpression: return BindTypeOf((TypeOfExpressionSyntax)node, diagnostics); @@ -1730,8 +1730,6 @@ private void GenerateExplicitConversionErrors( return; } - // PROTOTYPE(default) SHould handle default literal here? - if (conversion.ResultKind == LookupResultKind.OverloadResolutionFailure) { Debug.Assert(conversion.IsUserDefined); @@ -4834,7 +4832,6 @@ private BoundExpression BindMemberAccessWithBoundLeft( return BadExpression(node, boundLeft); } - // PROTOTYPE(default) unify with case above // No member accesses on default if (boundLeft.IsLiteralDefault()) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 01b4110d47dcb..d1d6def28ad6a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -401,6 +401,11 @@ private static bool ReportBadDynamicArguments( // error CS1978: Cannot use an expression of type '__arglist' as an argument to a dynamically dispatched operation Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArg, arg.Syntax, "__arglist"); } + else if (arg.IsLiteralDefault()) + { + Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArgDefault, arg.Syntax); + hasErrors = true; + } else { // Lambdas,anonymous methods and method groups are the typeless expressions that diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 63f89c29a4f69..0fe188dbf1aed 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -3019,8 +3019,6 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBa return new BoundAsOperator(node, operand, typeExpression, Conversion.NullLiteral, resultType); } - // PROTOTYPE(default) Something needed here for "as" operator - if (operand.Kind == BoundKind.MethodGroup) { Error(diagnostics, ErrorCode.ERR_NoExplicitBuiltinConv, node, MessageID.IDS_MethodGroup.Localize(), targetType); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index 8875b1bf3239e..437f825bb0120 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -711,6 +711,10 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r { diagnostics.Add(ErrorCode.ERR_NullNotValid, node.Location); } + else if (ultimateReceiver.IsLiteralDefault()) + { + diagnostics.Add(ErrorCode.ERR_NullNotValid, node.Location); + } else if (ultimateReceiver.Kind == BoundKind.Lambda || ultimateReceiver.Kind == BoundKind.UnboundLambda) { // Could not find an implementation of the query pattern for source type '{0}'. '{1}' not found. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 76b8c4cb21de7..b64a4cdecc6b8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1026,8 +1026,11 @@ private bool IsValidFixedVariableInitializer(TypeSymbol declType, SourceLocalSym initializerOpt = GenerateConversionForAssignment(declType, initializerOpt, diagnostics); if (!initializerOpt.HasAnyErrors) { - Debug.Assert(initializerOpt.Kind == BoundKind.Conversion && ((BoundConversion)initializerOpt).Operand.IsLiteralNull(), + Debug.Assert(initializerOpt.Kind == BoundKind.Conversion && + (((BoundConversion)initializerOpt).Operand.IsLiteralNull() || + ((BoundConversion)initializerOpt).Operand.IsLiteralDefault()), "All other typeless expressions should have conversion errors"); + // CONSIDER: this is a very confusing error message, but it's what Dev10 reports. Error(diagnostics, ErrorCode.ERR_FixedNotNeeded, initializerSyntax); } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index ae6f644b69e1f..0415972e4b517 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -975,7 +975,7 @@ internal static bool HasImplicitConstantExpressionConversion(BoundExpression sou { var constantValue = source.ConstantValue; - if (constantValue == null) + if (constantValue == null || constantValue.IsDefaultLiteral) { return false; } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index baff4f9c4c7da..bd7211c651e23 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -1339,6 +1339,15 @@ internal static string ERR_BadDynamicMethodArg { } } + /// + /// Looks up a localized string similar to Cannot use a type-inferred default operator as an argument to a dynamically dispatched operation.. + /// + internal static string ERR_BadDynamicMethodArgDefault { + get { + return ResourceManager.GetString("ERR_BadDynamicMethodArgDefault", 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.. /// @@ -4076,7 +4085,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 { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 72a6fb82dd841..c5ad7864023da 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2169,7 +2169,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 @@ -4956,4 +4956,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Out variable or pattern variable declarations are not allowed within constructor/field/auto-implemented property initializers. + + Cannot use a type-inferred default operator as an argument to a dynamically dispatched operation. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 8577a12e69e16..f8d101232b295 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1435,5 +1435,7 @@ internal enum ErrorCode ERR_OutVarDeconstructionIsNotSupported = 8199, ERR_ExpressionVariableInConstructorOrFieldInitializer = 8200, #endregion diagnostics for out var + + ERR_BadDynamicMethodArgDefault = 9000, } } diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index 60a9213fcaf52..d7de12e877c44 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -588,7 +588,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/Test/Semantic/Semantics/TargetTypedDefaultTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs index 42b8d8f8bc391..33e36621f4558 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs @@ -1,7 +1,9 @@ // 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 @@ -80,16 +82,13 @@ static void M() dynamic x3 = default; ITest x5 = default; T x6 = default; + System.Console.Write($""{x1} {x2} {x3} {x5} {x6}""); } } interface ITest { } "; var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); - comp.VerifyDiagnostics( - // (7,14): warning CS0219: The variable 'x2' is assigned but its value is never used - // int? x2 = default; - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x2").WithArguments("x2").WithLocation(7, 14) - ); + comp.VerifyDiagnostics(); } [Fact] @@ -258,7 +257,15 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0"); - // PROTOTYPE(default) Verify semantic model + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().Single(); + Assert.Null(model.GetTypeInfo(def).Type); + Assert.Equal("System.Int32", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); } [Fact] @@ -336,6 +343,16 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, 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().Single(); + Assert.Null(model.GetTypeInfo(def).Type); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Null(model.GetDeclaredSymbol(def)); } [Fact] @@ -370,7 +387,7 @@ class C comp.VerifyDiagnostics(); } - [Fact(Skip = "PROTOTYPE(default)")] + [Fact] public void ConstAndProperty() { string source = @" @@ -380,17 +397,16 @@ class C static int P { get { return default; } } static void Main() { - System.Console.Write($""{x} {P}""); + System.Console.Write($""{x}-{P}""); } } "; - // PROTOTYPE(default) There is a problem with treating default literal as constant (should the constant value in the literal or in the conversion, or somewhere else?) var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "0 0"); + CompileAndVerify(comp, expectedOutput: "0-0"); } - [Fact(Skip = "PROTOTYPE(default)")] + [Fact] public void DynamicInvocation() { string source = @" @@ -405,8 +421,259 @@ static void M1() "; var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); + comp.VerifyDiagnostics( + // (7,14): error CS9000: Cannot use a type-inferred default operator as an argument to a dynamically dispatched operation. + // d.M2(default); + Diagnostic(ErrorCode.ERR_BadDynamicMethodArgDefault, "default").WithLocation(7, 14) + ); + } + + [Fact] + public void DefaultEqualsDefault() + { + string source = @" +class C +{ + static void Main() + { + System.Console.Write($""{default == default} {default != default}""); + } +} +"; + + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, 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. + CreateCompilationWithMscorlib(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.ExperimentalParseOptions) + .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 = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,27): error CS0446: Foreach cannot operate on a 'default'. Did you intend to invoke the 'default'? + // foreach (int x in default) { } + Diagnostic(ErrorCode.ERR_AnonMethGrpInForEach, "default").WithArguments("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 = +@"static class C +{ + static void Main() + { + var q = from x in default select x; + } +} +"; + var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.ExperimentalParseOptions); + compilation.VerifyDiagnostics( + // (5,35): error CS0186: Use of null is not valid in this context + // var q = from x in default select x; + Diagnostic(ErrorCode.ERR_NullNotValid, "select x").WithLocation(5, 35) + ); + } + + [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.ExperimentalParseOptions, 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.ExperimentalParseOptions); + compilation.VerifyDiagnostics(); + } + + [Fact] + public void ThrowDefault() + { + var text = @" +class C +{ + static void Main() + { + throw default; + } +}"; + + var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions, 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 Main() + { + System.Console.Write(default as long); + } +}"; + + var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe); + 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) + ); + } + + [Fact] + public void CS0403ERR_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 = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); + 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.ExperimentalParseOptions); + 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.ExperimentalParseOptions, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - // PROTOTYPE(default) Crash + CompileAndVerify(comp, expectedOutput: "False"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single(); + Assert.Null(model.GetTypeInfo(def).Type); + Assert.Equal("System.Int32?", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(def).Symbol); } [Fact] @@ -450,7 +717,7 @@ static System.Func M() CompileAndVerify(comp, expectedOutput: "0"); } - [Fact(Skip = "PROTOTYPE(default)")] + [Fact] public void Switch() { string source = @" @@ -458,14 +725,16 @@ class C { static void Main() { - int x = 1; + M(0); + } + static void M(int x) + { switch (x) { - case default: // PROTOTYPE(default) default is not recognized as constant - default: + case default: + System.Console.Write(""default""); break; - case 1: - System.Console.Write(""1""); + default: break; } } @@ -474,7 +743,7 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "1"); + CompileAndVerify(comp, expectedOutput: "default"); } [Fact] @@ -597,7 +866,21 @@ static void Main() var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0"); - // PROTOTYPE(default) Verify semantic model. What is the type of default? + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var def = nodes.OfType().Single(); + Assert.Null(model.GetTypeInfo(def).Type); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Null(model.GetSymbolInfo(def).Symbol); + Assert.Null(model.GetDeclaredSymbol(def)); + + var conversion = nodes.OfType().Single(); + var conversionTypeInfo = model.GetTypeInfo(conversion); + Assert.Equal("System.Int16", conversionTypeInfo.Type.ToTestDisplayString()); + Assert.Equal("System.Int32", conversionTypeInfo.ConvertedType.ToTestDisplayString()); } [Fact] diff --git a/src/Compilers/Core/Portable/ConstantValue.cs b/src/Compilers/Core/Portable/ConstantValue.cs index 4d2d0f9705680..0e41a62342b0f 100644 --- a/src/Compilers/Core/Portable/ConstantValue.cs +++ b/src/Compilers/Core/Portable/ConstantValue.cs @@ -27,6 +27,7 @@ internal enum ConstantValueTypeDiscriminator : byte String, Decimal, DateTime, + DefaultLiteral, } internal abstract partial class ConstantValue : IEquatable @@ -79,6 +80,7 @@ internal abstract partial class ConstantValue : IEquatable public static ConstantValue Bad { get { return ConstantValueBad.Instance; } } public static ConstantValue Null { get { return ConstantValueNull.Instance; } } + public static ConstantValue DefaultLiteral { get { return ConstantValueDefault.DefaultLiteralConstant; } } public static ConstantValue Nothing { get { return Null; } } // Null, Nothing and Unset are all ConstantValueNull. Null and Nothing are equivalent and represent the null and // nothing constants in C# and VB. Unset indicates an uninitialized ConstantValue. @@ -655,6 +657,14 @@ public bool IsNull } } + public bool IsDefaultLiteral + { + get + { + return ReferenceEquals(this, DefaultLiteral); + } + } + public bool IsNothing { get diff --git a/src/Compilers/Core/Portable/ConstantValueSpecialized.cs b/src/Compilers/Core/Portable/ConstantValueSpecialized.cs index f751de9761288..fbd6868627828 100644 --- a/src/Compilers/Core/Portable/ConstantValueSpecialized.cs +++ b/src/Compilers/Core/Portable/ConstantValueSpecialized.cs @@ -269,6 +269,7 @@ private class ConstantValueDefault : ConstantValueDiscriminated public static readonly ConstantValueDefault Decimal = new ConstantValueDecimalZero(); public static readonly ConstantValueDefault DateTime = new ConstantValueDefault(ConstantValueTypeDiscriminator.DateTime); public static readonly ConstantValueDefault Boolean = new ConstantValueDefault(ConstantValueTypeDiscriminator.Boolean); + public static readonly ConstantValueDefault DefaultLiteralConstant = new ConstantValueDefault(ConstantValueTypeDiscriminator.DefaultLiteral); protected ConstantValueDefault(ConstantValueTypeDiscriminator discriminator) : base(discriminator) diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 2fea3168d7a18..83619985de9f7 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"); }); } From 79effee68bc57d7fd5e604ef67d46c4e37e9a0aa Mon Sep 17 00:00:00 2001 From: jcouv Date: Thu, 2 Feb 2017 22:54:34 -0800 Subject: [PATCH 2/6] PR feedback (1) --- .../Portable/Binder/Binder_Expressions.cs | 11 +++++++--- .../Portable/Binder/Binder_Statements.cs | 2 +- .../Semantics/Conversions/ConversionsBase.cs | 6 +++--- .../Portable/BoundTree/BoundExpression.cs | 2 +- .../BoundTree/BoundExpressionExtensions.cs | 4 ++-- .../CSharp/Portable/BoundTree/BoundNodes.xml | 2 +- .../Portable/BoundTree/BoundTreeVisitors.cs | 4 ++-- .../CSharp/Portable/BoundTree/Constructors.cs | 6 +++--- .../CSharp/Portable/BoundTree/Expression.cs | 2 +- .../CSharp/Portable/BoundTree/Formatting.cs | 2 +- .../CSharp/Portable/CodeGen/EmitExpression.cs | 6 +++--- .../CSharp/Portable/CodeGen/Optimizer.cs | 2 +- .../Portable/Compiler/MethodCompiler.cs | 2 +- .../Portable/FlowAnalysis/DataFlowPass.cs | 2 +- .../Portable/FlowAnalysis/FlowAnalysisPass.cs | 2 +- .../FlowAnalysis/PreciseAbstractFlowPass.cs | 2 +- .../CSharp/Portable/Lowering/Extensions.cs | 2 +- .../ExpressionLambdaRewriter.cs | 2 +- .../Lowering/LambdaRewriter/LambdaRewriter.cs | 2 +- .../LocalRewriter/LocalRewriter_AsOperator.cs | 2 +- .../LocalRewriter_BinaryOperator.cs | 12 +++++------ .../LocalRewriter/LocalRewriter_Call.cs | 10 +++++----- .../LocalRewriter/LocalRewriter_Conversion.cs | 20 +++++++++---------- .../LocalRewriter_ObjectCreationExpression.cs | 4 ++-- .../LocalRewriter_UnaryOperator.cs | 8 ++++---- .../MethodToStateMachineRewriter.cs | 2 +- .../Lowering/SyntheticBoundNodeFactory.cs | 2 +- .../Symbols/Source/ParameterHelpers.cs | 2 +- .../Test/Semantic/Semantics/UnsafeTests.cs | 2 +- .../Rewriters/LocalDeclarationRewriter.cs | 2 +- 30 files changed, 67 insertions(+), 62 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 972800d4fee55..b9b55cf887db8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -553,7 +553,7 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic return BindDefaultExpression((DefaultExpressionSyntax)node, diagnostics); case SyntaxKind.DefaultLiteral: - return new BoundDefaultOperator((DefaultLiteralSyntax)node, ConstantValue.DefaultLiteral, type: null); + return BindDefaultLiteral((DefaultLiteralSyntax)node); case SyntaxKind.TypeOfExpression: return BindTypeOf((TypeOfExpressionSyntax)node, diagnostics); @@ -656,6 +656,11 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic } } + private static BoundExpression BindDefaultLiteral(DefaultLiteralSyntax node) + { + return new BoundDefaultLiteral(node, ConstantValue.DefaultLiteral, type: null); + } + private BoundExpression BindTupleExpression(TupleExpressionSyntax node, DiagnosticBag diagnostics) { SeparatedSyntaxList arguments = node.Arguments; @@ -970,7 +975,7 @@ internal static ConstantValue GetConstantSizeOf(TypeSymbol type) private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, DiagnosticBag diagnostics) { TypeSymbol type = this.BindType(node.Type, diagnostics); - return new BoundDefaultOperator(node, type); + return new BoundDefaultLiteral(node, type); } /// @@ -5008,7 +5013,7 @@ private BoundExpression BindMemberAccessWithBoundLeft( private static void WarnOnAccessOfOffDefault(SyntaxNode node, BoundExpression boundLeft, DiagnosticBag diagnostics) { - if (boundLeft != null && boundLeft.Kind == BoundKind.DefaultOperator && boundLeft.ConstantValue == ConstantValue.Null) + if (boundLeft != null && boundLeft.Kind == BoundKind.DefaultLiteral && boundLeft.ConstantValue == ConstantValue.Null) { Error(diagnostics, ErrorCode.WRN_DotOnDefault, node, boundLeft.Type); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index b64a4cdecc6b8..88f5868b4940d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3176,7 +3176,7 @@ private BoundStatement BindReturn(ReturnStatementSyntax syntax, DiagnosticBag di var interactiveInitializerMethod = this.ContainingMemberOrLambda as SynthesizedInteractiveInitializerMethod; if (interactiveInitializerMethod != null) { - arg = new BoundDefaultOperator(interactiveInitializerMethod.GetNonNullSyntaxNode(), interactiveInitializerMethod.ResultType); + arg = new BoundDefaultLiteral(interactiveInitializerMethod.GetNonNullSyntaxNode(), interactiveInitializerMethod.ResultType); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 0415972e4b517..454abea10c92c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -815,8 +815,8 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi } break; - case BoundKind.DefaultOperator: - var defaultExpression = (BoundDefaultOperator)sourceExpression; + case BoundKind.DefaultLiteral: + var defaultExpression = (BoundDefaultLiteral)sourceExpression; if ((object)defaultExpression.Type == null) { return Conversion.DefaultLiteral; @@ -975,7 +975,7 @@ internal static bool HasImplicitConstantExpressionConversion(BoundExpression sou { var constantValue = source.ConstantValue; - if (constantValue == null || constantValue.IsDefaultLiteral) + if (constantValue == null || (object)source.Type == null) { return false; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index 8fd41a74eb37a..138bd51dd7edc 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs @@ -444,7 +444,7 @@ public override Symbol ExpressionSymbol } } - internal partial class BoundDefaultOperator + internal partial class BoundDefaultLiteral { public override ConstantValue ConstantValue { diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs index d45de4dc1a445..506c18f927825 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs @@ -16,7 +16,7 @@ public static bool IsLiteralNull(this BoundExpression node) public static bool IsLiteralDefault(this BoundExpression node) { - return node.Kind == BoundKind.DefaultOperator && (object)node.Type == null; + return node.Kind == BoundKind.DefaultLiteral && (object)node.Type == null; } // returns true when expression has no side-effects and produces @@ -27,7 +27,7 @@ public static bool IsLiteralDefault(this BoundExpression node) // after some folding/propagation/algebraic transformations. public static bool IsDefaultValue(this BoundExpression node) { - if (node.Kind == BoundKind.DefaultOperator) + if (node.Kind == BoundKind.DefaultLiteral) { return true; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 99e6375428808..4bde166f2628f 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -551,7 +551,7 @@ - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundTreeVisitors.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundTreeVisitors.cs index f84414e9fb59e..03e52f0af7d24 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.DefaultLiteral: + return VisitDefaultLiteral(node as BoundDefaultLiteral, 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 0294d2332147f..646dad071c250 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs @@ -523,14 +523,14 @@ public static BoundBlock SynthesizedNoLocals(SyntaxNode syntax, params BoundStat } } - internal partial class BoundDefaultOperator + internal partial class BoundDefaultLiteral { - public BoundDefaultOperator(SyntaxNode syntax, TypeSymbol type, bool hasErrors = false) + public BoundDefaultLiteral(SyntaxNode syntax, TypeSymbol type, bool hasErrors = false) : this(syntax, type.GetDefaultValue(), type, hasErrors) { } - public BoundDefaultOperator(SyntaxNode syntax) + public BoundDefaultLiteral(SyntaxNode syntax) : this(syntax, constantValueOpt: null, type: null, hasErrors: false) { } diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 05a70d4ffe108..a70800cb0b27a 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -937,7 +937,7 @@ public override TResult Accept(OperationVisitor OperationKind.DefaultValueExpression; diff --git a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs index d2028634f646c..f15b904b46630 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs @@ -120,7 +120,7 @@ public override object Display } } - internal partial class BoundDefaultOperator + internal partial class BoundDefaultLiteral { public override object Display { diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 3dc7d31c60224..959506d32d4de 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.DefaultLiteral: + EmitDefaultExpression((BoundDefaultLiteral)expression, used); break; case BoundKind.TypeOfOperator: @@ -2617,7 +2617,7 @@ private void EmitDefaultValue(TypeSymbol type, bool used, SyntaxNode syntaxNode) } } - private void EmitDefaultExpression(BoundDefaultOperator expression, bool used) + private void EmitDefaultExpression(BoundDefaultLiteral expression, bool used) { Debug.Assert(expression.Type.SpecialType == SpecialType.System_Decimal || expression.Type.GetDefaultValue() == null, "constant should be set on this expression"); diff --git a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs index df8df3a1017b3..57ec5b9ba15dc 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs @@ -1405,7 +1405,7 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node) if (node.OperatorKind.IsChecked() && node.OperatorKind.Operator() == UnaryOperatorKind.UnaryMinus) { var origStack = StackDepth(); - PushEvalStack(new BoundDefaultOperator(node.Syntax, node.Operand.Type), ExprContext.Value); + PushEvalStack(new BoundDefaultLiteral(node.Syntax, node.Operand.Type), ExprContext.Value); BoundExpression operand = (BoundExpression)this.Visit(node.Operand); return node.Update(node.OperatorKind, operand, node.ConstantValueOpt, node.MethodOpt, node.ResultKind, node.Type); } diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 978e76795edbf..b37b6db512f5a 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -1173,7 +1173,7 @@ private static BoundStatement ChainImplicitStructConstructor(MethodSymbol method new BoundAssignmentOperator( syntax, new BoundThisReference(syntax, containingType), - new BoundDefaultOperator(syntax, containingType), + new BoundDefaultLiteral(syntax, containingType), RefKind.None, containingType)); } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs index 184bad3c20ebd..aea6407bf9947 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs @@ -585,7 +585,7 @@ internal static bool WriteConsideredUse(TypeSymbol type, BoundExpression value) } return WriteConsideredUse(null, boundConversion.Operand); } - case BoundKind.DefaultOperator: + case BoundKind.DefaultLiteral: return false; case BoundKind.ObjectCreationExpression: var init = (BoundObjectCreationExpression)value; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs index f5855486af4ec..5b3a8a67bded9 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 BoundDefaultLiteral(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 880b2b51b3ff3..f8fc4ed4d50da 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs @@ -2449,7 +2449,7 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no return null; } - public override BoundNode VisitDefaultOperator(BoundDefaultOperator node) + public override BoundNode VisitDefaultLiteral(BoundDefaultLiteral node) { return null; } diff --git a/src/Compilers/CSharp/Portable/Lowering/Extensions.cs b/src/Compilers/CSharp/Portable/Lowering/Extensions.cs index 188a33818c2b2..00d6e932029b8 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.DefaultLiteral) { return true; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/ExpressionLambdaRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/ExpressionLambdaRewriter.cs index 995ab95aaf730..cdf83f4e4276c 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.DefaultLiteral: case BoundKind.HostObjectMemberReference: case BoundKind.Literal: case BoundKind.Local: diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs index 522716c6ff368..3d632fd106120 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs @@ -535,7 +535,7 @@ private T IntroduceFrame(BoundNode node, LambdaFrame frame, Func.Empty, sideEffects: ImmutableArray.Create(sideEffect), - value: new BoundDefaultOperator(syntax, null, type), + value: new BoundDefaultLiteral(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 BoundDefaultLiteral(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 BoundDefaultLiteral(syntax, null, nullableBoolType); } return new BoundObjectCreationExpression( syntax, @@ -1520,7 +1520,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 BoundDefaultLiteral(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 65d833375b4b7..fa2812189ab82 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -990,14 +990,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 BoundDefaultLiteral(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 BoundDefaultLiteral(syntax, parameterType); } else if (parameterType.IsNullableType()) { @@ -1053,20 +1053,20 @@ private BoundExpression GetDefaultParameterSpecial(SyntaxNode syntax, ParameterS if (parameter.IsMarshalAsObject) { // default(object) - defaultValue = new BoundDefaultOperator(syntax, parameter.Type); + defaultValue = new BoundDefaultLiteral(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 BoundDefaultLiteral(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 BoundDefaultLiteral(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 5fdfa94ddd887..b95fff56e7831 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -175,7 +175,7 @@ private BoundExpression MakeConversionNode( if (NullableNeverHasValue(rewrittenOperand)) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultLiteral(syntax, rewrittenType); } BoundExpression nullableValue = NullableAlwaysHasValue(rewrittenOperand); @@ -191,7 +191,7 @@ private BoundExpression MakeConversionNode( case ConversionKind.DefaultLiteral: if (!_inExpressionLambda || !explicitCastInCode) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultLiteral(syntax, rewrittenType); } break; @@ -200,7 +200,7 @@ private BoundExpression MakeConversionNode( case ConversionKind.ExplicitReference: if (rewrittenOperand.IsDefaultValue() && (!_inExpressionLambda || !explicitCastInCode)) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultLiteral(syntax, rewrittenType); } break; @@ -218,7 +218,7 @@ private BoundExpression MakeConversionNode( case ConversionKind.ExplicitNumeric: if (rewrittenOperand.IsDefaultValue() && (!_inExpressionLambda || !explicitCastInCode)) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultLiteral(syntax, rewrittenType); } if (rewrittenType.SpecialType == SpecialType.System_Decimal || rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal) @@ -271,7 +271,7 @@ private BoundExpression MakeConversionNode( rewrittenOperand.IsDefaultValue() && (!_inExpressionLambda || !explicitCastInCode)) { - return new BoundDefaultOperator(syntax, rewrittenType); + return new BoundDefaultLiteral(syntax, rewrittenType); } if (rewrittenType.SpecialType == SpecialType.System_Decimal) @@ -660,7 +660,7 @@ private static bool NullableNeverHasValue(BoundExpression expression) } // default(int?) never has a value. - if (expression.Kind == BoundKind.DefaultOperator) + if (expression.Kind == BoundKind.DefaultLiteral) { return true; } @@ -827,7 +827,7 @@ private BoundExpression RewriteFullyLiftedBuiltInConversion( conversion.UnderlyingConversions[0], type.GetNullableUnderlyingType(), @checked)); - BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); + BoundExpression alternative = new BoundDefaultLiteral(syntax, null, type); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, @@ -855,7 +855,7 @@ private BoundExpression OptimizeLiftedUserDefinedConversion( if (NullableNeverHasValue(operand)) { - return new BoundDefaultOperator(syntax, type); + return new BoundDefaultLiteral(syntax, type); } // If the converted expression is known to never be null then we can return @@ -884,7 +884,7 @@ private BoundExpression OptimizeLiftedBuiltInConversion( if (NullableNeverHasValue(operand)) { - return new BoundDefaultOperator(syntax, null, type); + return new BoundDefaultLiteral(syntax, null, type); } // Second, a trickier optimization. If the conversion is "(T?)(new S?(x))" then @@ -1060,7 +1060,7 @@ private BoundExpression RewriteLiftedUserDefinedConversion( BoundExpression consequence = MakeLiftedUserDefinedConversionConsequence(userDefinedCall, rewrittenType); // default(R?) - BoundExpression alternative = new BoundDefaultOperator(syntax, rewrittenType); + BoundExpression alternative = new BoundDefaultLiteral(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 8e15b32b82849..60a20a60eca65 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 BoundDefaultLiteral(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 BoundDefaultLiteral(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 d477f11a0898a..4e9e4731394cb 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 BoundDefaultLiteral(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 BoundDefaultLiteral(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 BoundDefaultLiteral(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 BoundDefaultLiteral(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 2983176a9268d..4d55fc9d48250 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -522,7 +522,7 @@ private BoundExpression HoistExpression( case BoundKind.ThisReference: case BoundKind.BaseReference: - case BoundKind.DefaultOperator: + case BoundKind.DefaultLiteral: return expr; default: diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index deb1c20e8bd88..f8066265f15b6 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -1163,7 +1163,7 @@ public BoundExpression Array(TypeSymbol elementType, BoundExpression length) internal BoundExpression Default(TypeSymbol type) { - return new BoundDefaultOperator(Syntax, type) { WasCompilerGenerated = true }; + return new BoundDefaultLiteral(Syntax, type) { WasCompilerGenerated = true }; } internal BoundStatement Try( diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 8cab368cc3423..dcfba6542d671 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -318,7 +318,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.DefaultLiteral) || (expression.Kind == BoundKind.ObjectCreationExpression && IsValidDefaultValue((BoundObjectCreationExpression)expression)); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 64ed014f8504c..8400c72a1df50 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, DefaultLiteral '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/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs index 4b56c9d2e4d5b..808b4c53a586c 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs @@ -104,7 +104,7 @@ private static BoundExpression GetCustomTypeInfoPayloadId(SyntaxNode syntax, Met { if (!hasCustomTypeInfoPayload) { - return new BoundDefaultOperator(syntax, guidConstructor.ContainingType); + return new BoundDefaultLiteral(syntax, guidConstructor.ContainingType); } var value = ConstantValue.Create(DynamicFlagsCustomTypeInfo.PayloadTypeId.ToString()); From 89b5d07becc36eaa08f6472f4df92c92251a325c Mon Sep 17 00:00:00 2001 From: jcouv Date: Mon, 13 Feb 2017 12:40:34 -0800 Subject: [PATCH 3/6] PR feedback (2) --- .../Portable/Binder/Binder_Operators.cs | 6 ++ .../Portable/Binder/ForEachLoopBinder.cs | 15 ++- .../Portable/CSharpResources.Designer.cs | 9 ++ .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../Semantics/TargetTypedDefaultTests.cs | 94 ++++++++++++++++++- 6 files changed, 119 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 0fe188dbf1aed..3ce0d39f49004 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -2628,6 +2628,12 @@ private BoundExpression BindIsOperator(BinaryExpressionSyntax node, DiagnosticBa HashSet useSiteDiagnostics = null; + if (operand.ConstantValue == ConstantValue.DefaultLiteral) + { + Error(diagnostics, ErrorCode.ERR_DefaultNotValid, node, targetType); + return new BoundIsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); + } + if (operand.ConstantValue == ConstantValue.Null || operand.Kind == BoundKind.MethodGroup || operand.Type.SpecialType == SpecialType.System_Void) diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 87e902682d579..ebba1f614f2ec 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -388,12 +388,17 @@ 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); - + 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); + } + else if (collectionExpr.ConstantValue.IsDefaultLiteral) + { + diagnostics.Add(ErrorCode.ERR_DefaultNotValid, _syntax.Expression.Location); + } return false; } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index bd7211c651e23..771e5cfb2d27f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -3166,6 +3166,15 @@ internal static string ERR_DefaultMemberOnIndexedType { } } + /// + /// Looks up a localized string similar to Use of default is not valid in this context. + /// + internal static string ERR_DefaultNotValid { + get { + return ResourceManager.GetString("ERR_DefaultNotValid", resourceCulture); + } + } + /// /// Looks up a localized string similar to Argument of type '{0}' is not applicable for the DefaultParameterValue attribute. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index c5ad7864023da..ac15020c40d5b 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -837,6 +837,9 @@ Use of null is not valid in this context + + Use of default is not valid in this context + The 'this' object cannot be used before all of its fields are assigned to diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index f8d101232b295..7ec143bba2773 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1437,5 +1437,6 @@ internal enum ErrorCode #endregion diagnostics for out var ERR_BadDynamicMethodArgDefault = 9000, + ERR_DefaultNotValid = 9001, } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs index 33e36621f4558..e5274843eccd2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs @@ -241,6 +241,57 @@ static void Main() 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; + System.Console.Write($""{x} {y}""); + } +} +"; + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); + 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) + ); + } + + [Fact] + public void UserDefinedStruct() + { + string source = @" +struct S { } +class C +{ + static void M() + { + const S x = default(S); + const S y = (S)default; + System.Console.Write($""{x} {y}""); + } +} +"; + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); + 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) + ); + } + [Fact] public void ImplicitlyTypedArray() { @@ -351,7 +402,6 @@ static void Main() var def = nodes.OfType().Single(); Assert.Null(model.GetTypeInfo(def).Type); Assert.Null(model.GetSymbolInfo(def).Symbol); - Assert.Null(model.GetSymbolInfo(def).Symbol); Assert.Null(model.GetDeclaredSymbol(def)); } @@ -371,6 +421,23 @@ static int M() comp.VerifyDiagnostics(); } + [Fact] + public void YieldReturn() + { + string source = @" +using System.Collections.Generic; +class C +{ + static IEnumerable M() + { + yield return default; + } +} +"; + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); + comp.VerifyDiagnostics(); + } + [Fact] public void ReturnNullableType() { @@ -490,9 +557,9 @@ static void Main() var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (6,27): error CS0446: Foreach cannot operate on a 'default'. Did you intend to invoke the 'default'? + // (6,27): error CS9001: Use of default is not valid in this context // foreach (int x in default) { } - Diagnostic(ErrorCode.ERR_AnonMethGrpInForEach, "default").WithArguments("default").WithLocation(6, 27), + Diagnostic(ErrorCode.ERR_DefaultNotValid, "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) @@ -594,6 +661,26 @@ static void Main() ); } + [Fact] + public void DefaultInIsPattern() + { + var text = @" +class C +{ + static void Main() + { + System.Console.Write(default is long); + } +}"; + + var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (6,30): error CS9001: Use of default is not valid in this context + // System.Console.Write(default is long); + Diagnostic(ErrorCode.ERR_DefaultNotValid, "default is long").WithArguments("long").WithLocation(6, 30) + ); + } + [Fact] public void CS0403ERR_TypeVarCanBeDefault() { @@ -874,7 +961,6 @@ static void Main() var def = nodes.OfType().Single(); Assert.Null(model.GetTypeInfo(def).Type); Assert.Null(model.GetSymbolInfo(def).Symbol); - Assert.Null(model.GetSymbolInfo(def).Symbol); Assert.Null(model.GetDeclaredSymbol(def)); var conversion = nodes.OfType().Single(); From 28dba9ec72a64fb162ab91d643911c29fa3f67c9 Mon Sep 17 00:00:00 2001 From: jcouv Date: Mon, 13 Feb 2017 15:16:31 -0800 Subject: [PATCH 4/6] More tests from feedback --- .../Portable/Binder/ForEachLoopBinder.cs | 3 ++- .../Semantics/TargetTypedDefaultTests.cs | 25 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index ebba1f614f2ec..b97db3579d11f 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -394,12 +394,13 @@ private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundE { // Spec seems to refer to null literals, but Dev10 reports anything known to be null. diagnostics.Add(ErrorCode.ERR_NullNotValid, _syntax.Expression.Location); + return false; } else if (collectionExpr.ConstantValue.IsDefaultLiteral) { diagnostics.Add(ErrorCode.ERR_DefaultNotValid, _syntax.Expression.Location); + return false; } - return false; } if ((object)collectionExprType == null) // There's no way to enumerate something without a type. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs index e5274843eccd2..956903995a09d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs @@ -251,7 +251,8 @@ static void M() { const T x = default(T); const T y = (T)default; - System.Console.Write($""{x} {y}""); + const object z = (T)default; + System.Console.Write($""{x} {y} {z}""); } } "; @@ -262,7 +263,10 @@ static void M() 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) + Diagnostic(ErrorCode.ERR_BadConstType, "T").WithArguments("T").WithLocation(7, 15), + // (8,26): error CS0134: 'z' is of type 'object'. A const field of a reference type other than string can only be initialized with null. + // const object z = (T)default; + Diagnostic(ErrorCode.ERR_NotNullConstRefField, "(T)default").WithArguments("z", "object").WithLocation(8, 26) ); } @@ -277,18 +281,22 @@ static void M() { const S x = default(S); const S y = (S)default; - System.Console.Write($""{x} {y}""); + const object z = (S)default; + System.Console.Write($""{x} {y} {z}""); } } "; var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); comp.VerifyDiagnostics( - // (7,15): error CS0283: The type 'S' cannot be declared const + // (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) + Diagnostic(ErrorCode.ERR_BadConstType, "S").WithArguments("S").WithLocation(8, 15), + // (9,26): error CS0134: 'z' is of type 'object'. A const field of a reference type other than string can only be initialized with null. + // const object z = (S)default; + Diagnostic(ErrorCode.ERR_NotNullConstRefField, "(S)default").WithArguments("z", "object").WithLocation(9, 26) ); } @@ -425,6 +433,7 @@ static int M() public void YieldReturn() { string source = @" +using System.Collections; using System.Collections.Generic; class C { @@ -432,6 +441,10 @@ static IEnumerable M() { yield return default; } + static IEnumerable M2() + { + yield return default; + } } "; var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); @@ -682,7 +695,7 @@ static void Main() } [Fact] - public void CS0403ERR_TypeVarCanBeDefault() + public void TypeVarCanBeDefault() { var source = @"interface I { } From 79353becfe202072dadb3612efddb7a74c75bf1a Mon Sep 17 00:00:00 2001 From: jcouv Date: Tue, 14 Feb 2017 09:23:36 -0800 Subject: [PATCH 5/6] Adding test for array construction --- .../Semantics/TargetTypedDefaultTests.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs index 956903995a09d..286b99780cd3e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs @@ -347,6 +347,22 @@ static void Main() ); } + [Fact] + public void ArrayConstruction() + { + string source = @" +class C +{ + static void Main() + { + var t = new object[default]; + } +} +"; + var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions); + comp.VerifyDiagnostics(); + } + [Fact] public void Tuple() { From af7bb3db8daf4edf1b4fa9c572a423a0cf2a0d02 Mon Sep 17 00:00:00 2001 From: jcouv Date: Tue, 14 Feb 2017 09:29:55 -0800 Subject: [PATCH 6/6] Using wrong error message for default in query --- src/Compilers/CSharp/Portable/Binder/Binder_Query.cs | 2 +- .../CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index 437f825bb0120..532cafb8f1c73 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -713,7 +713,7 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r } else if (ultimateReceiver.IsLiteralDefault()) { - diagnostics.Add(ErrorCode.ERR_NullNotValid, node.Location); + diagnostics.Add(ErrorCode.ERR_DefaultNotValid, node.Location); } else if (ultimateReceiver.Kind == BoundKind.Lambda || ultimateReceiver.Kind == BoundKind.UnboundLambda) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs index 286b99780cd3e..1b06f0cb954da 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs @@ -609,9 +609,9 @@ static void Main() "; var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.ExperimentalParseOptions); compilation.VerifyDiagnostics( - // (5,35): error CS0186: Use of null is not valid in this context + // (5,35): error CS9001: Use of default is not valid in this context // var q = from x in default select x; - Diagnostic(ErrorCode.ERR_NullNotValid, "select x").WithLocation(5, 35) + Diagnostic(ErrorCode.ERR_DefaultNotValid, "select x").WithLocation(5, 35) ); }