From ef29793624f7cbde18b2297102b1318df11e9dde Mon Sep 17 00:00:00 2001 From: vsadov Date: Fri, 16 Jun 2017 17:19:46 -0700 Subject: [PATCH] Fix for https://github.com/dotnet/roslyn/issues/20226 --- .../Portable/Binder/ForEachLoopBinder.cs | 15 +- .../Portable/CSharpResources.Designer.cs | 9 ++ .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../Test/Semantic/Semantics/ForEachTests.cs | 15 +- .../Semantics/SpanStackSafetyTests.cs | 128 ++++++++++++++++++ 6 files changed, 160 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 65f01d851c992..3e3d79dfa51a6 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -326,12 +326,12 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, ImmutableArray originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions; if (originalUserDefinedConversions.Length > 1) { - diagnostics.Add(ErrorCode.ERR_AmbigUDConv, _syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType); + diagnostics.Add(ErrorCode.ERR_AmbigUDConv, foreachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType); } else { SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType); - diagnostics.Add(ErrorCode.ERR_NoExplicitConv, _syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second); + diagnostics.Add(ErrorCode.ERR_NoExplicitConv, foreachKeyword.GetLocation(), distinguisher.First, distinguisher.Second); } hasErrors = true; } @@ -346,7 +346,16 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics); builder.CurrentConversion = this.Conversions.ClassifyConversionFromType(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics); - builder.EnumeratorConversion = this.Conversions.ClassifyConversionFromType(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, _syntax), ref useSiteDiagnostics); + var getEnumeratorType = builder.GetEnumeratorMethod.ReturnType; + // we never convert struct enumerators to object - it is done only for null-checks. + builder.EnumeratorConversion = getEnumeratorType.IsValueType? + Conversion.Identity: + this.Conversions.ClassifyConversionFromType(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, _syntax), ref useSiteDiagnostics); + + if (getEnumeratorType.IsRestrictedType() && (IsDirectlyInIterator || IsInAsyncMethod())) + { + diagnostics.Add(ErrorCode.ERR_BadSpecialByRefIterator, foreachKeyword.GetLocation(), builder.CollectionType); + } diagnostics.Add(_syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index d138014aed482..a5852ab4ce5bd 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -1906,6 +1906,15 @@ internal static string ERR_BadSourceCodeKind { } } + /// + /// Looks up a localized string similar to foreach statement cannot operate on variables of type '{0}' in async or iterator methods.. + /// + internal static string ERR_BadSpecialByRefIterator { + get { + return ResourceManager.GetString("ERR_BadSpecialByRefIterator", resourceCulture); + } + } + /// /// Looks up a localized string similar to Parameters or locals of type '{0}' cannot be declared in async methods or lambda expressions.. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 2d2b01faaeaf6..f81197e8b6380 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3617,6 +3617,9 @@ Give the compiler some way to differentiate the methods. For example, you can gi Parameters or locals of type '{0}' cannot be declared in async methods or lambda expressions. + + foreach statement cannot operate on variables of type '{0}' in async or iterator methods. + Security attribute '{0}' cannot be applied to an Async method. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 8bdd35fe19190..abd60a388893e 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1514,5 +1514,7 @@ internal enum ErrorCode ERR_AutoPropsInRoStruct = 8515, ERR_FieldlikeEventsInRoStruct = 8516, ERR_RefStructInterfaceImpl = 8517, + ERR_BadSpecialByRefIterator = 8518, + } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs index 33b698a9e3832..cf62be1b229c1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs @@ -1319,7 +1319,7 @@ struct Enumerator Assert.False(info.NeedsDisposeMethod); // Definitely not disposable Assert.Equal(ConversionKind.Identity, info.CollectionConversion.Kind); Assert.Equal(ConversionKind.Identity, info.CurrentConversion.Kind); - Assert.Equal(ConversionKind.Boxing, info.EnumeratorConversion.Kind); + Assert.Equal(ConversionKind.Identity, info.EnumeratorConversion.Kind); Assert.Equal(ConversionKind.ImplicitNumeric, boundNode.ElementConversion.Kind); Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString()); @@ -1847,7 +1847,7 @@ struct Enumerator Assert.False(info.NeedsDisposeMethod); // Definitely not disposable Assert.Equal(ConversionKind.Identity, info.CollectionConversion.Kind); Assert.Equal(ConversionKind.Identity, info.CurrentConversion.Kind); - Assert.Equal(ConversionKind.Boxing, info.EnumeratorConversion.Kind); + Assert.Equal(ConversionKind.Identity, info.EnumeratorConversion.Kind); Assert.Equal(ConversionKind.ImplicitNumeric, boundNode.ElementConversion.Kind); Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString()); @@ -3188,13 +3188,10 @@ static void Main() var comp4 = CreateCompilation(source4, new[] { comp2.ToMetadataReference(), comp3.ToMetadataReference() }); comp4.VerifyDiagnostics( - // (6,9): error CS0012: The type 'ValueType' is defined in an assembly that is not referenced. You must add a reference to assembly 'MissingBaseType1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // foreach (var x in new Enumerable()) - Diagnostic(ErrorCode.ERR_NoTypeDef, @"foreach (var x in new Enumerable()) - { }").WithArguments("System.ValueType", "MissingBaseType1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 9), - // (6,9): error CS0012: The type 'ValueType' is defined in an assembly that is not referenced. You must add a reference to assembly 'MissingBaseType1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // foreach (var x in new Enumerable()) - Diagnostic(ErrorCode.ERR_NoTypeDef, "foreach").WithArguments("System.ValueType", "MissingBaseType1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 9) + // (6,9): error CS0012: The type 'ValueType' is defined in an assembly that is not referenced. You must add a reference to assembly 'MissingBaseType1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // foreach (var x in new Enumerable()) + Diagnostic(ErrorCode.ERR_NoTypeDef, @"foreach (var x in new Enumerable()) + { }").WithArguments("System.ValueType", "MissingBaseType1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 9) ); } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs index da6a85c2a46de..08a95b7979323 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs @@ -450,6 +450,134 @@ public void Dispose() { } ); } + [WorkItem(20226, "https://github.com/dotnet/roslyn/issues/20226")] + [Fact] + public void RefIteratorInAsync() + { + var text = @" +using System; +using System.Threading.Tasks; + +class Program +{ + static void Main(string[] args) + { + } + + static async Task Test() + { + var obj = new C1(); + + foreach (var i in obj) + { + await Task.Yield(); + System.Console.WriteLine(i); + } + + return 123; + } +} + +class C1 +{ + public S1 GetEnumerator() + { + return new S1(); + } + + public ref struct S1 + { + public int Current => throw new NotImplementedException(); + + public bool MoveNext() + { + throw new NotImplementedException(); + } + } +} + +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (15,9): error CS8518: foreach statement cannot operate on variables of type 'C1' in async or iterator methods. + // foreach (var i in obj) + Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C1").WithLocation(15, 9) + ); + } + + [WorkItem(20226, "https://github.com/dotnet/roslyn/issues/20226")] + [Fact] + public void RefIteratorInIterator() + { + var text = @" +using System; +using System.Collections.Generic; + +class Program +{ + static void Main(string[] args) + { + // this is valid + Action a = () => + { + foreach (var i in new C1()) + { + } + }; + + a(); + } + + static IEnumerable Test() + { + // this is valid + Action a = () => + { + foreach (var i in new C1()) + { + } + }; + + a(); + + // this is an error + foreach (var i in new C1()) + { + } + + yield return 1; + } +} + +class C1 +{ + public S1 GetEnumerator() + { + return new S1(); + } + + public ref struct S1 + { + public int Current => throw new NotImplementedException(); + + public bool MoveNext() + { + throw new NotImplementedException(); + } + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (33,9): error CS8518: foreach statement cannot operate on variables of type 'C1' in async or iterator methods. + // foreach (var i in new C1()) + Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C1").WithLocation(33, 9) + ); + } [Fact] public void Properties()