diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs index e59f2daef5f8d..6ea9c6c769f4c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -95,13 +94,18 @@ internal BoundExpression BindDeconstruction(AssignmentExpressionSyntax node, Dia DeconstructionVariable locals = BindDeconstructionVariables(left, diagnostics, ref declaration, ref expression); Debug.Assert(locals.HasNestedVariables); - BoundExpression boundRight = rightPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue); - boundRight = FixTupleLiteral(locals.NestedVariables, boundRight, deconstruction, diagnostics); + var deconstructionDiagnostics = new DiagnosticBag(); + BoundExpression boundRight = rightPlaceholder ?? BindValue(right, deconstructionDiagnostics, BindValueKind.RValue); + if (!deconstructionDiagnostics.HasAnyErrors()) + { + boundRight = FixTupleLiteral(locals.NestedVariables, boundRight, deconstruction, deconstructionDiagnostics); + } bool resultIsUsed = resultIsUsedOverride || IsDeconstructionResultUsed(left); - var assignment = BindDeconstructionAssignment(deconstruction, left, boundRight, locals.NestedVariables, resultIsUsed, diagnostics); + var assignment = BindDeconstructionAssignment(deconstruction, left, boundRight, locals.NestedVariables, resultIsUsed, deconstructionDiagnostics); DeconstructionVariable.FreeDeconstructionVariables(locals.NestedVariables); + diagnostics.AddRange(deconstructionDiagnostics); return assignment; } @@ -143,7 +147,7 @@ internal BoundExpression BindDeconstruction(AssignmentExpressionSyntax node, Dia FailRemainingInferencesAndSetValEscape(checkedVariables, diagnostics, rightEscape); - var lhsTuple = DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: hasErrors || !resultIsUsed); + var lhsTuple = DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: diagnostics.HasAnyErrors() || !resultIsUsed); TypeSymbol returnType = hasErrors ? CreateErrorType() : lhsTuple.Type; uint leftEscape = GetBroadestValEscape(lhsTuple, this.LocalScopeDepth); @@ -453,6 +457,7 @@ private static TypeSymbol MakeMergedTupleType(ArrayBuilder.GetInstance(leftLength); + var locationsBuilder = ArrayBuilder.GetInstance(leftLength); for (int i = 0; i < rightLength; i++) { BoundExpression element = rhsLiteral.Arguments[i]; @@ -493,29 +498,29 @@ private static TypeSymbol MakeMergedTupleType(ArrayBuilder t == null)) { typesBuilder.Free(); + locationsBuilder.Free(); return null; } // The tuple created here is not identical to the one created by // DeconstructionVariablesAsTuple. It represents a smaller // tree of types used for figuring out natural types in tuple literal. - // Therefore, we do not check constraints here as it would report errors - // that are already reported later. DeconstructionVariablesAsTuple - // constructs the final tuple type and checks constraints. return TupleTypeSymbol.Create( locationOpt: null, elementTypes: typesBuilder.ToImmutableAndFree(), - elementLocations: default(ImmutableArray), + elementLocations: locationsBuilder.ToImmutableAndFree(), elementNames: default(ImmutableArray), compilation: compilation, diagnostics: diagnostics, - shouldCheckConstraints: false, - errorPositions: default(ImmutableArray)); + shouldCheckConstraints: true, + errorPositions: default(ImmutableArray), + syntax: syntax); } private BoundTupleLiteral DeconstructionVariablesAsTuple(CSharpSyntaxNode syntax, ArrayBuilder variables, diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs index e8dc27f904426..fca8dbe0ba300 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs @@ -4596,10 +4596,7 @@ static void Main() Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(5, 17), // (5,31): error CS8210: A tuple may not contain a value of type 'void'. // (int x, void y) = (1, Main()); - Diagnostic(ErrorCode.ERR_VoidInTuple, "Main()").WithLocation(5, 31), - // (5,17): error CS0029: Cannot implicitly convert type 'void' to 'void' - // (int x, void y) = (1, Main()); - Diagnostic(ErrorCode.ERR_NoImplicitConv, "void y").WithArguments("void", "void").WithLocation(5, 17) + Diagnostic(ErrorCode.ERR_VoidInTuple, "Main()").WithLocation(5, 31) ); var main = comp.GetMember("C.Main"); var tree = comp.SyntaxTrees[0]; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index 6b16317388525..df0d9f64a6a8b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -2447,7 +2447,7 @@ public class C public void M(ref Span global) { Span local = stackalloc int[10]; - (M(), M()) = local; + (M(), M()) = local; // error } public static void Main() => throw null; public ref Span M() => throw null; @@ -2459,7 +2459,7 @@ public static class Extensions "; CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics( // (9,22): error CS8352: Cannot use local 'local' in this context because it may expose referenced variables outside of their declaration scope - // (M(), M()) = local; + // (M(), M()) = local; // error Diagnostic(ErrorCode.ERR_EscapeLocal, "local").WithArguments("local").WithLocation(9, 22), // warning CS1685: The predefined type 'ExtensionAttribute' is defined in multiple assemblies in the global alias; using definition from 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Diagnostic(ErrorCode.WRN_MultiplePredefTypes).WithArguments("System.Runtime.CompilerServices.ExtensionAttribute", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089").WithLocation(1, 1) @@ -2509,7 +2509,7 @@ public void M(ref Span global) { Span local = stackalloc int[10]; (global, global) = global; - (global, global) = local; + (global, global) = local; // error } public static void Main() => throw null; } @@ -2520,7 +2520,7 @@ public static class Extensions "; CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics( // (10,28): error CS8352: Cannot use local 'local' in this context because it may expose referenced variables outside of their declaration scope - // (global, global) = local; + // (global, global) = local; // error Diagnostic(ErrorCode.ERR_EscapeLocal, "local").WithArguments("local").WithLocation(10, 28), // warning CS1685: The predefined type 'ExtensionAttribute' is defined in multiple assemblies in the global alias; using definition from 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Diagnostic(ErrorCode.WRN_MultiplePredefTypes).WithArguments("System.Runtime.CompilerServices.ExtensionAttribute", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089").WithLocation(1, 1) @@ -2537,7 +2537,7 @@ public class C { public void M(ref Span global) { - var t = ((global, global) = global); + var t = ((global, global) = global); // error } public static void Main() => throw null; } @@ -2561,10 +2561,10 @@ public ValueTuple(T1 item1, T2 item2) }"; CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics( // (8,19): error CS0306: The type 'Span' may not be used as a type argument - // var t = ((global, global) = global); + // var t = ((global, global) = global); // error Diagnostic(ErrorCode.ERR_BadTypeArgument, "global").WithArguments("System.Span").WithLocation(8, 19), // (8,27): error CS0306: The type 'Span' may not be used as a type argument - // var t = ((global, global) = global); + // var t = ((global, global) = global); // error Diagnostic(ErrorCode.ERR_BadTypeArgument, "global").WithArguments("System.Span").WithLocation(8, 27), // warning CS1685: The predefined type 'ExtensionAttribute' is defined in multiple assemblies in the global alias; using definition from 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Diagnostic(ErrorCode.WRN_MultiplePredefTypes).WithArguments("System.Runtime.CompilerServices.ExtensionAttribute", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089").WithLocation(1, 1) @@ -2582,7 +2582,9 @@ public class C public void M(ref Span global) { Span local = stackalloc int[10]; - (global, global) = (local, local); + string s; + (global, global) = (local, local); // error 1 + (global, s) = (local, """"); // error 2 } public static void Main() => throw null; } @@ -2602,12 +2604,58 @@ public ValueTuple(T1 item1, T2 item2) } "; CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics( - // (9,29): error CS0306: The type 'Span' may not be used as a type argument - // (global, global) = (local, local); - Diagnostic(ErrorCode.ERR_BadTypeArgument, "local").WithArguments("System.Span").WithLocation(9, 29), - // (9,36): error CS0306: The type 'Span' may not be used as a type argument - // (global, global) = (local, local); - Diagnostic(ErrorCode.ERR_BadTypeArgument, "local").WithArguments("System.Span").WithLocation(9, 36) + // (10,29): error CS0306: The type 'Span' may not be used as a type argument + // (global, global) = (local, local); // error 1 + Diagnostic(ErrorCode.ERR_BadTypeArgument, "local").WithArguments("System.Span").WithLocation(10, 29), + // (10,36): error CS0306: The type 'Span' may not be used as a type argument + // (global, global) = (local, local); // error 1 + Diagnostic(ErrorCode.ERR_BadTypeArgument, "local").WithArguments("System.Span").WithLocation(10, 36), + // (11,24): error CS0306: The type 'Span' may not be used as a type argument + // (global, s) = (local, ""); // error 2 + Diagnostic(ErrorCode.ERR_BadTypeArgument, "local").WithArguments("System.Span").WithLocation(11, 24) + ); + } + + [Fact] + public void DeconstructionAssignmentOfTypelessTuple() + { + var text = @" +using System; + +public class C +{ + public void M(ref Span global) + { + Span local = stackalloc int[10]; + Span local2 = stackalloc int[10]; + string s; + (global, s) = (local, null); // error 1 + (local2, s) = (local, null); // error 2 + } + public static void Main() => throw null; +} +namespace System +{ + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + + public ValueTuple(T1 item1, T2 item2) + { + this.Item1 = item1; + this.Item2 = item2; + } + } +} +"; + CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics( + // (11,24): error CS0306: The type 'Span' may not be used as a type argument + // (global, s) = (local, null); // error 1 + Diagnostic(ErrorCode.ERR_BadTypeArgument, "local").WithArguments("System.Span").WithLocation(11, 24), + // (12,24): error CS0306: The type 'Span' may not be used as a type argument + // (local2, s) = (local, null); // error 2 + Diagnostic(ErrorCode.ERR_BadTypeArgument, "local").WithArguments("System.Span").WithLocation(12, 24) ); } @@ -2622,7 +2670,7 @@ public class C public void M(ref Span global) { Span local = stackalloc int[10]; - (global, global) = (local, local); + (global, global) = (local, local); // error } public static void Main() => throw null; } @@ -2644,10 +2692,10 @@ public ValueTuple(T1 item1, T2 item2) "; CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics( // (9,29): error CS0306: The type 'Span' may not be used as a type argument - // (global, global) = (local, local); + // (global, global) = (local, local); // error Diagnostic(ErrorCode.ERR_BadTypeArgument, "local").WithArguments("System.Span").WithLocation(9, 29), // (9,36): error CS0306: The type 'Span' may not be used as a type argument - // (global, global) = (local, local); + // (global, global) = (local, local); // error Diagnostic(ErrorCode.ERR_BadTypeArgument, "local").WithArguments("System.Span").WithLocation(9, 36) ); } @@ -2698,8 +2746,8 @@ public void M(ref Span global) { Span local = stackalloc int[10]; var (local1, local2) = local; - global = local1; - global = local2; + global = local1; // error 1 + global = local2; // error 2 var (local3, local4) = global; global = local3; @@ -2714,10 +2762,10 @@ public static class Extensions "; CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics( // (10,18): error CS8352: Cannot use local 'local1' in this context because it may expose referenced variables outside of their declaration scope - // global = local1; + // global = local1; // error 1 Diagnostic(ErrorCode.ERR_EscapeLocal, "local1").WithArguments("local1").WithLocation(10, 18), // (11,18): error CS8352: Cannot use local 'local2' in this context because it may expose referenced variables outside of their declaration scope - // global = local2; + // global = local2; // error 2 Diagnostic(ErrorCode.ERR_EscapeLocal, "local2").WithArguments("local2").WithLocation(11, 18), // warning CS1685: The predefined type 'ExtensionAttribute' is defined in multiple assemblies in the global alias; using definition from 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Diagnostic(ErrorCode.WRN_MultiplePredefTypes).WithArguments("System.Runtime.CompilerServices.ExtensionAttribute", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089").WithLocation(1, 1) @@ -2737,7 +2785,7 @@ public void M(ref S global) S localCollection = stackalloc int[10]; foreach (var local in localCollection) { - global = local; + global = local; // error } } public static void Main() => throw null; @@ -2753,7 +2801,7 @@ public void M(ref S global) "; CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics( // (11,22): error CS8352: Cannot use local 'local' in this context because it may expose referenced variables outside of their declaration scope - // global = local; + // global = local; // error Diagnostic(ErrorCode.ERR_EscapeLocal, "local").WithArguments("local").WithLocation(11, 22) ); } diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs index 551a999bf7ed5..e84cfc8193c18 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs @@ -4953,24 +4953,68 @@ static void Main() } [Fact] - public void TupleLiteralElement004() + public void TupleLiteralElement004_WithoutValueTuple() { var source = @" +class C +{ + static void Main() + { + (short X, string Y) = (Alice: 1, Bob: null); + } +} +"; -using System; + var compilation = CreateStandardCompilation(source); + compilation.VerifyDiagnostics( + // (6,31): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported, or is declared in multiple referenced assemblies + // (short X, string Y) = (Alice: 1, Bob: null); + Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(Alice: 1, Bob: null)").WithArguments("System.ValueTuple`2").WithLocation(6, 31) + ); + var tree = compilation.SyntaxTrees[0]; + var decl = (ArgumentSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.Argument)); + var model = compilation.GetSemanticModel(tree); + Assert.Null(model.GetDeclaredSymbol(decl)); + } + [Fact] + public void TupleLiteralElement004() + { + var source = +@" class C { - static void Main() - { + static void Main() + { (short X, string Y) = (Alice: 1, Bob: null); } } +namespace System +{ + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + public ValueTuple(T1 item1, T2 item2) + { + this.Item1 = item1; + this.Item2 = item2; + } + } +} "; var compilation = CreateStandardCompilation(source); + compilation.VerifyDiagnostics( + // (6,32): warning CS8123: The tuple element name 'Alice' is ignored because a different name or no name is specified by the target type '(short, string)'. + // (short X, string Y) = (Alice: 1, Bob: null); + Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "Alice: 1").WithArguments("Alice", "(short, string)").WithLocation(6, 32), + // (6,42): warning CS8123: The tuple element name 'Bob' is ignored because a different name or no name is specified by the target type '(short, string)'. + // (short X, string Y) = (Alice: 1, Bob: null); + Diagnostic(ErrorCode.WRN_TupleLiteralNameMismatch, "Bob: null").WithArguments("Bob", "(short, string)").WithLocation(6, 42) + ); var tree = compilation.SyntaxTrees[0]; var decl = (ArgumentSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.Argument)); var model = compilation.GetSemanticModel(tree);