From c67384deca66c3e298ea1bcc387851cc4b5d5da8 Mon Sep 17 00:00:00 2001 From: vsadov Date: Wed, 5 Apr 2017 16:17:39 -0700 Subject: [PATCH 1/2] Implements the initial set of span-safety checks. These are mostly the checks dealing with stack-only nature of the Span. With minor difference they follow the same logic as the existing checks for types such as TypedReference. ==== Not in this change: Defining and detecting span-like types is NYI. For now we just treat any type named "System.Span" and "System.ReadonlySpan" as span-like. This will change. Some of the checks result in somewhat generaic messages and happen at emit phase. That was ok when the failures were supposed to be rare. Error clarity is not the goal of this change, but we will examone what errors should say and whether they should be moved to an earlier phase. --- .../Semantics/Conversions/Conversions.cs | 7 + .../Lowering/LambdaRewriter/LambdaRewriter.cs | 1 + .../Symbols/Source/ParameterHelpers.cs | 3 +- .../Source/SourceDelegateMethodSymbol.cs | 2 +- .../Symbols/Source/SourceMemberFieldSymbol.cs | 1 + .../Source/SourceMemberMethodSymbol.cs | 3 +- .../Symbols/Source/SourcePropertySymbol.cs | 2 +- .../SourceUserDefinedOperatorSymbolBase.cs | 4 +- .../CSharp/Portable/Symbols/TypeSymbol.cs | 25 + .../Portable/Symbols/TypeSymbolExtensions.cs | 9 +- .../CSharpCompilerSemanticTest.csproj | 1 + .../Semantics/SpanStackSafetyTests.cs | 597 ++++++++++++++++++ 12 files changed, 648 insertions(+), 7 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 98e6d0fc2435d..9f34b91c4b4ce 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -318,6 +318,13 @@ private static Conversion ToConversion(OverloadResolutionResult re return Conversion.NoConversion; } + //PROTOTYPE(span): generalize to all restricted types or it would be a compat break? + //cannot capture span-like types. + if (!method.IsStatic && methodGroup.Receiver.Type.IsSpanLikeType()) + { + return Conversion.NoConversion; + } + if (method.OriginalDefinition.ContainingType.SpecialType == SpecialType.System_Nullable_T && !method.IsOverride) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs index 8e3d3bb7fab1f..2ae4a8f1b4dc5 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs @@ -355,6 +355,7 @@ private void MakeFrames(ArrayBuilder closureDebugInfo) proxies.Add(captured, new CapturedToFrameSymbolReplacement(hoistedField, isReusable: false)); CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(frame, hoistedField); + //PROTOTYPE(span): move the check to the binding? if (hoistedField.Type.IsRestrictedType()) { foreach (CSharpSyntaxNode syntax in _analysis.CapturedVariables[captured]) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index c3a6fb15bb89f..beb39de4d7538 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -329,7 +329,8 @@ private static void ReportParameterErrors( Location loc = parameterSyntax.Identifier.GetNextToken(includeZeroWidth: true).GetLocation(); //could be missing diagnostics.Add(ErrorCode.ERR_DefaultValueBeforeRequiredValue, loc); } - else if (parameter.RefKind != RefKind.None && parameter.Type.IsRestrictedType()) + else if (parameter.RefKind != RefKind.None && + parameter.Type.IsRestrictedType(ignoreSpanLikeTypes: parameter.RefKind == RefKind.RefReadOnly)) { // CS1601: Cannot make reference to variable of type 'System.TypedReference' diagnostics.Add(ErrorCode.ERR_MethodArgCantBeRefAny, parameterSyntax.Location, parameter.Type); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs index 8949fc3ed2d2d..5a2e92e7576b0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs @@ -51,7 +51,7 @@ internal static void AddDelegateMembers( var objectType = binder.GetSpecialType(SpecialType.System_Object, diagnostics, syntax); var intPtrType = binder.GetSpecialType(SpecialType.System_IntPtr, diagnostics, syntax); - if (returnType.IsRestrictedType()) + if (returnType.IsRestrictedType(ignoreSpanLikeTypes: true)) { // Method or delegate cannot return type '{0}' diagnostics.Add(ErrorCode.ERR_MethodReturnCantBeRefAny, returnTypeSyntax.Location, returnType); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 0628f3c447090..9724632e40db6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -50,6 +50,7 @@ protected void TypeChecks(TypeSymbol type, DiagnosticBag diagnostics) { diagnostics.Add(ErrorCode.ERR_FieldCantHaveVoidType, TypeSyntax.Location); } + //PROTOTYPE(span): span-like instance fields are allowed in span-like types, for now disallow always else if (type.IsRestrictedType()) { diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, TypeSyntax.Location, type); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 7f0a1e0ce42ec..9fab9ad81371e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -173,7 +173,8 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB var returnTypeSyntax = syntax.ReturnType.SkipRef(out refKind); _lazyReturnType = signatureBinder.BindType(returnTypeSyntax, diagnostics); - if (_lazyReturnType.IsRestrictedType()) + // span-like types are returnable in general + if (_lazyReturnType.IsRestrictedType(ignoreSpanLikeTypes: true)) { if (_lazyReturnType.SpecialType == SpecialType.System_TypedReference && (this.ContainingType.SpecialType == SpecialType.System_TypedReference || this.ContainingType.SpecialType == SpecialType.System_ArgIterator)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index b2d09df8410e8..6a1c05bc14683 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -1298,7 +1298,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok var conversions = new TypeConversions(this.ContainingAssembly.CorLibrary); this.Type.CheckAllConstraints(conversions, _location, diagnostics); - if (this.Type.IsRestrictedType()) + if (this.Type.IsRestrictedType(ignoreSpanLikeTypes: !this.IsAutoProperty)) { diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, this.CSharpSyntaxNode.Type.Location, this.Type); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 2906a890e9ab8..ffb05eba3d606 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -146,7 +146,9 @@ protected override void MethodChecks(DiagnosticBag diagnostics) _lazyReturnType = signatureBinder.BindType(ReturnTypeSyntax, diagnostics); - if (_lazyReturnType.IsRestrictedType()) + // restricted types cannot be returned. + // NOTE: Span-like types can be returned (if expression is returnable). + if (_lazyReturnType.IsRestrictedType(ignoreSpanLikeTypes: true)) { // Method or delegate cannot return type '{0}' diagnostics.Add(ErrorCode.ERR_MethodReturnCantBeRefAny, ReturnTypeSyntax.Location, _lazyReturnType); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index ea5c79a6b760e..32f944d899d1d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -639,6 +639,31 @@ public virtual NamedTypeSymbol TupleUnderlyingType /// internal abstract bool IsManagedType { get; } + //PROTOTYPE(span): this will completely change depending on how span-like types are implemented. + // Span and ReadOnlySpan will be special types (currently they are not always) + // Users may be able to define their own Spanlike types, but that is completely NYI + // For now we will be simply looking at "System.Span" and "System.ReadOnlySpan" names + /// + /// Returns true if the type is a Span/ReadOnlySpan + /// + internal bool IsSpanLikeType() + { + var originalDef = this.OriginalDefinition; + + if (originalDef.Name != "Span" && originalDef.Name != "ReadonlySpan") + { + return false; + } + + var ns = originalDef.ContainingSymbol as NamespaceSymbol; + if (ns?.Name != "System") + { + return false; + } + + return (object)ns.ContainingNamespace != null; + } + #region ITypeSymbol Members INamedTypeSymbol ITypeSymbol.BaseType diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 8741b49294134..d8f08c7b5831a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -917,9 +917,11 @@ internal static bool IsValidV6SwitchGoverningType(this TypeSymbol type, bool isT /// /// Returns true if the type is one of the restricted types, namely: , /// , or . + /// or a ref-like type. /// #pragma warning restore RS0010 - internal static bool IsRestrictedType(this TypeSymbol type) + internal static bool IsRestrictedType(this TypeSymbol type, + bool ignoreSpanLikeTypes = false) { // See Dev10 C# compiler, "type.cpp", bool Type::isSpecialByRefType() const Debug.Assert((object)type != null); @@ -930,7 +932,10 @@ internal static bool IsRestrictedType(this TypeSymbol type) case SpecialType.System_RuntimeArgumentHandle: return true; } - return false; + + return ignoreSpanLikeTypes? + false: + type.IsSpanLikeType(); } public static bool IsIntrinsicType(this TypeSymbol type) diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index 3fb671de0394c..62ce92fbba0f6 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -133,6 +133,7 @@ + diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs new file mode 100644 index 0000000000000..e21a526b4ad7c --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs @@ -0,0 +1,597 @@ +// 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 System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + /// + /// this place is dedicated to binding related error tests + /// + public class SpanStackSafetyTests : CompilingTestBase + { + private static string spanSource = @" + +namespace System +{ + public struct Span + { + public ref T this[int i] => throw null; + public override int GetHashCode() => 1; + } + + public struct ReadonlySpan + { + public ref readonly T this[int i] => throw null; + public override int GetHashCode() => 2; + } +} +"; + //PROTOTYPE(span): this will be updated when rules for defining span are implemented + // most likely we would just pick the actual binary/corlib where + // span lives. + private static CSharpCompilation CreateCompilationWithMscorlibAndSpan(string text, CSharpCompilationOptions options = null) + { + var textWitSpan = new string[] { text, spanSource }; + var comp = CreateCompilationWithMscorlib45( + textWitSpan, + references: new List() { MscorlibRef_v4_0_30316_17626, SystemCoreRef, CSharpRef }, + options: options ?? TestOptions.ReleaseExe); + + return comp; + } + + [Fact] + public void TrivialBoxing() + { + var text = @" +using System; + +class Program +{ + static void Main() + { + object x = new Span(); + object y = new ReadonlySpan(); + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (8,20): error CS0029: Cannot implicitly convert type 'System.Span' to 'object' + // object x = new Span(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "new Span()").WithArguments("System.Span", "object").WithLocation(8, 20), + // (9,20): error CS0029: Cannot implicitly convert type 'System.ReadonlySpan' to 'object' + // object y = new ReadonlySpan(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "new ReadonlySpan()").WithArguments("System.ReadonlySpan", "object").WithLocation(9, 20) + ); + } + + [Fact] + public void LambdaCapturing() + { + var text = @" +using System; + +class Program +{ + // this should be ok + public delegate Span D1(Span arg); + + static void Main() + { + var x = new Span(); + + D1 d = (t)=>t; + x = d(x); + + // error due to capture + Func f = () => x[1]; + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + //PROTOTYPE(span): make this bind-time diagnostic? + comp.VerifyEmitDiagnostics( + // (17,29): error CS4013: Instance of type 'Span' cannot be used inside an anonymous function, query expression, iterator block or async method + // Func f = () => x[1]; + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "x").WithArguments("System.Span").WithLocation(17, 29) + ); + } + + [Fact] + public void GenericArgsAndConstraints() + { + var text = @" +using System; + +class Program +{ + static void Main() + { + var x = new Span(); + + Func> d = ()=>x; + } + + class C1 where T: Span + { + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + //PROTOTYPE(span): make this bind-time diagnostic? + comp.VerifyDiagnostics( + // (13,26): error CS0701: 'Span' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter. + // class C1 where T: Span + Diagnostic(ErrorCode.ERR_BadBoundType, "Span").WithArguments("System.Span").WithLocation(13, 26), + // (10,14): error CS0306: The type 'Span' may not be used as a type argument + // Func> d = ()=>x; + Diagnostic(ErrorCode.ERR_BadTypeArgument, "Span").WithArguments("System.Span").WithLocation(10, 14) + ); + } + + [Fact] + public void Arrays() + { + var text = @" +using System; + +class Program +{ + static void Main() + { + var x = new Span[1]; + + var y = new Span[1,2]; + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + //PROTOTYPE(span): make this bind-time diagnostic? + comp.VerifyDiagnostics( + // (8,21): error CS0611: Array elements cannot be of type 'Span' + // var x = new Span[1]; + Diagnostic(ErrorCode.ERR_ArrayElementCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(8, 21), + // (10,21): error CS0611: Array elements cannot be of type 'Span' + // var y = new Span[1,2]; + Diagnostic(ErrorCode.ERR_ArrayElementCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(10, 21) + ); + } + + [Fact] + public void ByrefParam() + { + var text = @" +using System; + +class Program +{ + static void Main() + { + } + + static void M1(ref Span ss) + { + } + + static void M2(out Span ss) + { + } + + // OK + static void M3(in Span ss) + { + } + + // technically ok, but what would you return? + static ref Span M4() => throw null; + + //OK + static ref readonly Span M5() => throw null; +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (10,20): error CS1601: Cannot make reference to variable of type 'Span' + // static void M1(ref Span ss) + Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "ref Span ss").WithArguments("System.Span").WithLocation(10, 20), + // (14,20): error CS1601: Cannot make reference to variable of type 'Span' + // static void M2(out Span ss) + Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "out Span ss").WithArguments("System.Span").WithLocation(14, 20) + ); + } + + [Fact] + public void Fields() + { + var text = @" +using System; + +public class Program +{ + static void Main() + { + } + + public static Span fs; + public Span fi; + + public struct S1 + { + public static Span fs; + public static Span fi; + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (16,23): error CS0610: Field or property cannot be of type 'Span' + // public static Span fi; + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(16, 23), + // (15,23): error CS0610: Field or property cannot be of type 'Span' + // public static Span fs; + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(15, 23), + // (10,19): error CS0610: Field or property cannot be of type 'Span' + // public static Span fs; + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span"), + // (11,12): error CS0610: Field or property cannot be of type 'Span' + // public Span fi; + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span") + ); + } + + [Fact] + public void Properties() + { + var text = @" +using System; + +public class Program +{ + static void Main() + { + } + + // valid + public static Span ps => default(Span); + public Span pi => default(Span); + + public Span this[int i] => default(Span); + + // not valid + public static Span aps {get;} + public Span api {get; set;} +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (17,19): error CS0610: Field or property cannot be of type 'Span' + // public static Span aps {get;} + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(17, 19), + // (18,12): error CS0610: Field or property cannot be of type 'Span' + // public Span api {get; set;} + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(18, 12) + ); + } + + [Fact] + public void Operators() + { + var text = @" +using System; + +public class Program +{ + static void Main() + { + } + + // valid + public static Span operator +(Span x, Program y) => default(Span); + + // invalid (baseline w/ TypedReference) + public static TypedReference operator +(Span x, Program y) => default(TypedReference); + +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (14,19): error CS1599: Method or delegate cannot return type 'TypedReference' + // public static TypedReference operator +(Span x, Program y) => default(TypedReference); + Diagnostic(ErrorCode.ERR_MethodReturnCantBeRefAny, "TypedReference").WithArguments("System.TypedReference").WithLocation(14, 19) + ); + } + + [Fact] + public void AsyncParams() + { + var text = @" +using System; +using System.Threading.Tasks; + +public class Program +{ + static void Main() + { + } + + public static async Task M1(Span arg) + { + await Task.Yield(); + return 42; + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (11,48): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or lambda expressions. + // public static async Task M1(Span arg) + Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "arg").WithArguments("System.Span").WithLocation(11, 48) + ); + } + + [Fact] + public void AsyncLocals() + { + var text = @" +using System; +using System.Threading.Tasks; + +public class Program +{ + static void Main() + { + } + + public static async Task M1() + { + Span local = default(Span); + + await Task.Yield(); + return 42; + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (13,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or lambda expressions. + // Span local = default(Span); + Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(13, 9), + // (13,19): warning CS0219: The variable 'local' is assigned but its value is never used + // Span local = default(Span); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local").WithArguments("local").WithLocation(13, 19) + ); + + comp = CreateCompilationWithMscorlibAndSpan(text, TestOptions.DebugExe); + + comp.VerifyDiagnostics( + // (13,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or lambda expressions. + // Span local = default(Span); + Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(13, 9), + // (13,19): warning CS0219: The variable 'local' is assigned but its value is never used + // Span local = default(Span); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local").WithArguments("local").WithLocation(13, 19) + ); + } + + [Fact] + public void AsyncSpilling() + { + var text = @" +using System; +using System.Threading.Tasks; + +public class Program +{ + static void Main() + { + } + + public static async Task M1() + { + // this is ok + TakesSpan(default(Span), 123); + + // this is not ok + TakesSpan(default(Span), await I1()); + + // this is ok + TakesSpan(await I1(), default(Span)); + + return 42; + } + + public static void TakesSpan(Span s, int i) + { + } + + public static void TakesSpan(int i, Span s) + { + } + + public static async Task I1() + { + await Task.Yield(); + return 42; + } + +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + //PROTOTYPE(span): spilling diagnostics is very hard to detect early. + // it would be uncommon too. Is it ok to do in Emit? + comp.VerifyEmitDiagnostics( + // (17,39): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' + // TakesSpan(default(Span), await I1()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span") + ); + + comp = CreateCompilationWithMscorlibAndSpan(text, TestOptions.DebugExe); + + comp.VerifyEmitDiagnostics( + // (17,39): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' + // TakesSpan(default(Span), await I1()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span") + ); + } + + [Fact] + public void AsyncSpillTemp() + { + var text = @" +using System; +using System.Threading.Tasks; + +public class Program +{ + static void Main() + { + } + + public static async Task M1() + { + // this is not ok + TakesSpan(s: default(Span), i: await I1()); + + return 42; + } + + public static void TakesSpan(int i, Span s) + { + } + + public static async Task I1() + { + await Task.Yield(); + return 42; + } + +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + //PROTOTYPE(span): spilling diagnostics is very hard to detect early. + // it would be uncommon too. Is it ok to do in Emit? + comp.VerifyEmitDiagnostics( + // (14,45): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' + // TakesSpan(s: default(Span), i: await I1()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span").WithLocation(14, 45) + ); + + comp = CreateCompilationWithMscorlibAndSpan(text, TestOptions.DebugExe); + + //PROTOTYPE(span): spilling diagnostics is very hard to detect early. + // it would be uncommon too. Is it ok to do in Emit? + comp.VerifyEmitDiagnostics( + // (14,45): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' + // TakesSpan(s: default(Span), i: await I1()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span").WithLocation(14, 45) + ); + } + + [Fact] + public void BaseMethods() + { + var text = @" +using System; + +public class Program +{ + static void Main() + { + // this is ok (overriden) + default(Span).GetHashCode(); + + // this is ok (implicit boxing) + default(Span).GetType(); + + // this is not ok (implicit boxing) + default(Span).ToString(); + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyDiagnostics( + // (12,9): error CS0029: Cannot implicitly convert type 'System.Span' to 'object' + // default(Span).GetType(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "default(Span)").WithArguments("System.Span", "object").WithLocation(12, 9), + // (15,9): error CS0029: Cannot implicitly convert type 'System.Span' to 'System.ValueType' + // default(Span).ToString(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "default(Span)").WithArguments("System.Span", "System.ValueType").WithLocation(15, 9) + ); + } + + [Fact] + public void MethodConversion() + { + var text = @" +using System; + +public class Program +{ + static void Main() + { + //PROTOTYPE(span): we allow this. Is that because it would be a breaking change? + Func d0 = default(TypedReference).GetHashCode; + + // none of the following is ok, since we would need to capture the receiver. + Func d1 = default(Span).GetHashCode; + + Func d2 = default(Span).GetType; + + Func d3 = default(Span).ToString; + } +} +"; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyEmitDiagnostics( + // (12,43): error CS0123: No overload for 'GetHashCode' matches delegate 'Func' + // Func d1 = default(Span).GetHashCode; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "GetHashCode").WithArguments("GetHashCode", "System.Func").WithLocation(12, 43), + // (14,44): error CS0123: No overload for 'GetType' matches delegate 'Func' + // Func d2 = default(Span).GetType; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "GetType").WithArguments("GetType", "System.Func").WithLocation(14, 44), + // (16,46): error CS0123: No overload for 'ToString' matches delegate 'Func' + // Func d3 = default(Span).ToString; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "ToString").WithArguments("ToString", "System.Func").WithLocation(16, 46) + ); + } + } +} From 0c8cdb7719f8a790803c7039bd0ad4f16848646c Mon Sep 17 00:00:00 2001 From: vsadov Date: Thu, 6 Apr 2017 23:54:22 -0700 Subject: [PATCH 2/2] Allow span-like instance fields in structs. This will be allowed only for span-like containers eventually, when we can declare those. For now allow in any struct. --- .../Symbols/Source/SourceMemberFieldSymbol.cs | 4 ++-- .../Symbols/Source/SourcePropertySymbol.cs | 1 + .../Semantic/Semantics/SpanStackSafetyTests.cs | 18 ++++++++---------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 9724632e40db6..1bfb2f5c9a691 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -50,8 +50,8 @@ protected void TypeChecks(TypeSymbol type, DiagnosticBag diagnostics) { diagnostics.Add(ErrorCode.ERR_FieldCantHaveVoidType, TypeSyntax.Location); } - //PROTOTYPE(span): span-like instance fields are allowed in span-like types, for now disallow always - else if (type.IsRestrictedType()) + //PROTOTYPE(span): span-like instance fields are allowed in span-like types, for now allow inany struct + else if (type.IsRestrictedType(ignoreSpanLikeTypes: !this.IsStatic && containingType.IsStructType())) { diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, TypeSyntax.Location, type); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index 6a1c05bc14683..a25df4812d2a4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -1298,6 +1298,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok var conversions = new TypeConversions(this.ContainingAssembly.CorLibrary); this.Type.CheckAllConstraints(conversions, _location, diagnostics); + //PROTOTYPE(span): allow in span-like structs? if (this.Type.IsRestrictedType(ignoreSpanLikeTypes: !this.IsAutoProperty)) { diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, this.CSharpSyntaxNode.Type.Location, this.Type); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs index e21a526b4ad7c..ca1f2e75294fd 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs @@ -238,8 +238,9 @@ static void Main() public struct S1 { - public static Span fs; - public static Span fi; + //PROTOTYPE(span): instance Span fields in structs are ok for now, - until span-like types can be declared + public static Span fs1; + public Span fi1; } } "; @@ -247,18 +248,15 @@ public struct S1 CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); comp.VerifyDiagnostics( - // (16,23): error CS0610: Field or property cannot be of type 'Span' - // public static Span fi; - Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(16, 23), - // (15,23): error CS0610: Field or property cannot be of type 'Span' - // public static Span fs; - Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(15, 23), + // (16,23): error CS0610: Field or property cannot be of type 'Span' + // public static Span fs1; + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(16, 23), // (10,19): error CS0610: Field or property cannot be of type 'Span' // public static Span fs; - Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span"), + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(10, 19), // (11,12): error CS0610: Field or property cannot be of type 'Span' // public Span fi; - Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span") + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "Span").WithArguments("System.Span").WithLocation(11, 12) ); }