diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index c4f1e0c15f0f4..c2d0eba493a98 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.CodeGen; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -19,6 +20,145 @@ namespace Microsoft.CodeAnalysis.CSharp { internal partial class Binder { +#nullable enable + + private enum EscapeLevel : uint + { + CallingMethod = Binder.CallingMethodScope, + ReturnOnly = Binder.ReturnOnlyScope, + } + + /// + /// The destination in a method arguments must match (MAMM) check. This is + /// created primarily for ref and out arguments of a ref struct. It also applies + /// to function pointer this and arglist arguments. + /// + private readonly struct MixableDestination + { + internal BoundExpression Argument { get; } + + /// + /// In the case this is the argument for a ref / out parameter this will refer + /// to the corresponding parameter. This will be null in cases like arguments + /// passed to an arglist. + /// + internal ParameterSymbol? Parameter { get; } + + /// + /// This destination can only be written to by arguments that have an equal or + /// wider escape level. An destination that is + /// can never be written to by an argument that has a level of . + /// + internal EscapeLevel EscapeLevel { get; } + + internal MixableDestination(ParameterSymbol parameter, BoundExpression argument) + { + Debug.Assert(parameter.RefKind.IsWritableReference() && parameter.Type.IsRefLikeType); + Debug.Assert(GetParameterValEscapeLevel(parameter).HasValue); + Argument = argument; + Parameter = parameter; + EscapeLevel = GetParameterValEscapeLevel(parameter)!.Value; + } + + internal MixableDestination(BoundExpression argument, EscapeLevel escapeLevel) + { + Argument = argument; + Parameter = null; + EscapeLevel = escapeLevel; + } + + internal bool IsAssignableFrom(EscapeLevel level) => EscapeLevel switch + { + EscapeLevel.CallingMethod => level == EscapeLevel.CallingMethod, + EscapeLevel.ReturnOnly => true, + _ => throw ExceptionUtilities.UnexpectedValue(EscapeLevel) + }; + + public override string? ToString() => (Parameter, Argument, EscapeLevel).ToString(); + } + + /// + /// Represents an argument being analyzed for escape analysis purposes. This represents the + /// argument as written. For example a `ref x` will only be represented by a single + /// . + /// + private readonly struct EscapeArgument + { + /// + /// This will be null in cases like arglist or a function pointer receiver. + /// + internal ParameterSymbol? Parameter { get; } + + internal BoundExpression Argument { get; } + + internal RefKind RefKind { get; } + + internal EscapeArgument(ParameterSymbol? parameter, BoundExpression argument, RefKind refKind, bool isArgList = false) + { + Debug.Assert(!isArgList || parameter is null); + Argument = argument; + Parameter = parameter; + RefKind = refKind; + } + + public void Deconstruct(out ParameterSymbol? parameter, out BoundExpression argument, out RefKind refKind) + { + parameter = Parameter; + argument = Argument; + refKind = RefKind; + } + + public override string? ToString() => Parameter is { } p + ? p.ToString() + : Argument.ToString(); + } + + /// + /// Represents a value being analyzed for escape analysis purposes. This represents the value + /// as it contributes to escape analysis which means arguments can show up multiple times. For + /// example `ref x` will be represented as both a val and ref escape. + /// + private readonly struct EscapeValue + { + /// + /// This will be null in cases like arglist or a function pointer receiver. + /// + internal ParameterSymbol? Parameter { get; } + + internal BoundExpression Argument { get; } + + /// + /// This is _only_ useful when calculating MAMM as it dictates to what level the value + /// escaped to. That allows it to be filtered against the parameters it could possibly + /// write to. + /// + internal EscapeLevel EscapeLevel { get; } + + internal bool IsRefEscape { get; } + + internal EscapeValue(ParameterSymbol? parameter, BoundExpression argument, EscapeLevel escapeLevel, bool isRefEscape) + { + Argument = argument; + Parameter = parameter; + EscapeLevel = escapeLevel; + IsRefEscape = isRefEscape; + } + + public void Deconstruct(out ParameterSymbol? parameter, out BoundExpression argument, out EscapeLevel escapeLevel, out bool isRefEscape) + { + parameter = Parameter; + argument = Argument; + escapeLevel = EscapeLevel; + isRefEscape = IsRefEscape; + } + + public override string? ToString() => Parameter is { } p + ? p.ToString() + : Argument.ToString(); + } + +#nullable disable + /// /// For the purpose of escape verification we operate with the depth of local scopes. /// The depth is a uint, with smaller number representing shallower/wider scopes. @@ -782,39 +922,47 @@ private bool CheckParameterValueKind(SyntaxNode node, BoundParameter parameter, return true; } - private uint GetParameterValEscape(ParameterSymbol parameter) + private static EscapeLevel? EscapeLevelFromScope(uint scope) => scope switch { - if (UseUpdatedEscapeRules) - { - return parameter.EffectiveScope == DeclarationScope.ValueScoped ? - Binder.CurrentMethodScope : - Binder.CallingMethodScope; - } - else + Binder.ReturnOnlyScope => EscapeLevel.ReturnOnly, + Binder.CallingMethodScope => EscapeLevel.CallingMethod, + _ => null, + }; + + private static uint GetParameterValEscape(ParameterSymbol parameter) + { + return parameter switch { - return Binder.CallingMethodScope; - } + { EffectiveScope: DeclarationScope.ValueScoped } => Binder.CurrentMethodScope, + { RefKind: RefKind.Out, UseUpdatedEscapeRules: true } => Binder.ReturnOnlyScope, + _ => Binder.CallingMethodScope + }; } - private uint GetParameterRefEscape(ParameterSymbol parameter) + private static EscapeLevel? GetParameterValEscapeLevel(ParameterSymbol parameter) => + EscapeLevelFromScope(GetParameterValEscape(parameter)); + + private static uint GetParameterRefEscape(ParameterSymbol parameter) { return parameter switch { - { RefKind: RefKind.None } or { EffectiveScope: not DeclarationScope.Unscoped } => Binder.CurrentMethodScope, - { Type.IsRefLikeType: true } => Binder.ReturnOnlyScope, - _ => Binder.CallingMethodScope + { RefKind: RefKind.None } => Binder.CurrentMethodScope, + { EffectiveScope: DeclarationScope.RefScoped } => Binder.CurrentMethodScope, + _ => Binder.ReturnOnlyScope }; } - private bool CheckParameterValEscape(SyntaxNode node, BoundParameter parameter, uint escapeTo, BindingDiagnosticBag diagnostics) + private static EscapeLevel? GetParameterRefEscapeLevel(ParameterSymbol parameter) => + EscapeLevelFromScope(GetParameterRefEscape(parameter)); + + private bool CheckParameterValEscape(SyntaxNode node, ParameterSymbol parameter, uint escapeTo, BindingDiagnosticBag diagnostics) { Debug.Assert(escapeTo is Binder.CallingMethodScope or Binder.ReturnOnlyScope); if (UseUpdatedEscapeRules) { - var parameterSymbol = parameter.ParameterSymbol; - if (GetParameterValEscape(parameterSymbol) > escapeTo) + if (GetParameterValEscape(parameter) > escapeTo) { - Error(diagnostics, this.InUnsafeRegion ? ErrorCode.WRN_EscapeVariable : ErrorCode.ERR_EscapeVariable, node, parameterSymbol); + Error(diagnostics, this.InUnsafeRegion ? ErrorCode.WRN_EscapeVariable : ErrorCode.ERR_EscapeVariable, node, parameter); return this.InUnsafeRegion; } return true; @@ -1503,7 +1651,7 @@ bool isRefEscape //by default it is safe to escape uint escapeScope = Binder.CallingMethodScope; - var argsAndParams = ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, RefKind RefKind)>.GetInstance(); + var escapeArguments = ArrayBuilder.GetInstance(); GetInvocationArgumentsForEscape( symbol, receiver: null, // receiver handled explicitly below @@ -1514,11 +1662,12 @@ bool isRefEscape // ref kinds of varargs are not interesting here. // __refvalue is not ref-returnable, so ref varargs can't come back from a call ignoreArglistRefKinds: true, - argsAndParams); + mixableArguments: null, + escapeArguments); try { - foreach (var (parameter, argument, effectiveRefKind) in argsAndParams) + foreach (var (parameter, argument, effectiveRefKind) in escapeArguments) { // ref escape scope is the narrowest of // - ref escape of all byref arguments @@ -1542,7 +1691,7 @@ bool isRefEscape } finally { - argsAndParams.Free(); + escapeArguments.Free(); } // check receiver if ref-like @@ -1567,7 +1716,7 @@ private uint GetInvocationEscapeWithUpdatedRules( //by default it is safe to escape uint escapeScope = Binder.CallingMethodScope; - var argsAndParamsAll = ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, bool IsRefEscape)>.GetInstance(); + var argsAndParamsAll = ArrayBuilder.GetInstance(); GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( symbol, receiver, @@ -1576,7 +1725,6 @@ private uint GetInvocationEscapeWithUpdatedRules( argRefKindsOpt, argsToParamsOpt, isRefEscape, - checkingMethodArgumentsMustMatch: false, ignoreArglistRefKinds: true, // https://github.com/dotnet/roslyn/issues/63325: for compatibility with C#10 implementation. argsAndParamsAll); foreach (var argAndParam in argsAndParamsAll) @@ -1642,7 +1790,7 @@ bool isRefEscape receiver = null; } - var argsAndParams = ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, RefKind RefKind)>.GetInstance(); + var escapeArguments = ArrayBuilder.GetInstance(); GetInvocationArgumentsForEscape( symbol, receiver: null, // receiver handled explicitly below @@ -1653,11 +1801,12 @@ bool isRefEscape // ref kinds of varargs are not interesting here. // __refvalue is not ref-returnable, so ref varargs can't come back from a call ignoreArglistRefKinds: true, - argsAndParams); + mixableArguments: null, + escapeArguments); try { - foreach (var (parameter, argument, effectiveRefKind) in argsAndParams) + foreach (var (parameter, argument, effectiveRefKind) in escapeArguments) { // ref escape scope is the narrowest of // - ref escape of all byref arguments @@ -1679,7 +1828,7 @@ bool isRefEscape } finally { - argsAndParams.Free(); + escapeArguments.Free(); } // check receiver if ref-like @@ -1707,7 +1856,7 @@ private bool CheckInvocationEscapeWithUpdatedRules( { bool result = true; - var argsAndParamsAll = ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, bool IsRefEscape)>.GetInstance(); + var argsAndParamsAll = ArrayBuilder.GetInstance(); GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( symbol, receiver, @@ -1716,7 +1865,6 @@ private bool CheckInvocationEscapeWithUpdatedRules( argRefKindsOpt, argsToParamsOpt, isRefEscape, - checkingMethodArgumentsMustMatch: false, ignoreArglistRefKinds: true, // https://github.com/dotnet/roslyn/issues/63325: for compatibility with C#10 implementation. argsAndParamsAll); foreach (var argAndParam in argsAndParamsAll) @@ -1759,11 +1907,18 @@ private static void GetInvocationArgumentsForEscape( ImmutableArray argRefKindsOpt, ImmutableArray argsToParamsOpt, bool ignoreArglistRefKinds, - ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, RefKind RefKind)> argsAndParams) + ArrayBuilder? mixableArguments, + ArrayBuilder escapeArguments) { if (receiver is { }) { - argsAndParams.Add(getReceiver(symbol, receiver)); + var tuple = getReceiver(symbol, receiver); + escapeArguments.Add(tuple); + + if (mixableArguments is not null && isMixableParameter(tuple.Parameter)) + { + mixableArguments.Add(new MixableDestination(tuple.Parameter, receiver)); + } } if (!argsOpt.IsDefault) @@ -1779,7 +1934,8 @@ private static void GetInvocationArgumentsForEscape( getArgList( argList.Arguments, ignoreArglistRefKinds ? default : argList.ArgumentRefKindsOpt, - argsAndParams); + mixableArguments, + escapeArguments); break; } @@ -1787,6 +1943,11 @@ private static void GetInvocationArgumentsForEscape( parameters[argsToParamsOpt.IsDefault ? argIndex : argsToParamsOpt[argIndex]] : null; + if (mixableArguments is not null && isMixableParameter(parameter)) + { + mixableArguments.Add(new MixableDestination(parameter, argument)); + } + var refKind = parameter?.RefKind ?? RefKind.None; if (!argRefKindsOpt.IsDefault) { @@ -1798,15 +1959,20 @@ private static void GetInvocationArgumentsForEscape( refKind = RefKind.In; } - argsAndParams.Add((parameter, argument, refKind)); + escapeArguments.Add(new EscapeArgument(parameter, argument, refKind)); } } - static (ParameterSymbol? Parameter, BoundExpression Argument, RefKind RefKind) getReceiver(Symbol symbol, BoundExpression receiver) + static bool isMixableParameter([NotNullWhen(true)] ParameterSymbol? parameter) => + parameter is not null && + parameter.Type.IsRefLikeType && + parameter.RefKind.IsWritableReference(); + + static EscapeArgument getReceiver(Symbol symbol, BoundExpression receiver) { if (symbol is FunctionPointerMethodSymbol) { - return (null, receiver, RefKind.None); + return new EscapeArgument(parameter: null, receiver, RefKind.None); } var method = symbol switch { @@ -1824,19 +1990,25 @@ private static void GetInvocationArgumentsForEscape( refKind = thisParameter.RefKind; } - return (thisParameter, receiver, refKind); + return new EscapeArgument(thisParameter, receiver, refKind); } static void getArgList( ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, - ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, RefKind RefKind)> argsAndParams) + ArrayBuilder? mixableArguments, + ArrayBuilder escapeArguments) { for (int argIndex = 0; argIndex < argsOpt.Length; argIndex++) { var argument = argsOpt[argIndex]; var refKind = argRefKindsOpt.IsDefault ? RefKind.None : argRefKindsOpt[argIndex]; - argsAndParams.Add((null, argument, refKind)); + escapeArguments.Add(new EscapeArgument(parameter: null, argument, refKind, isArgList: true)); + + if (refKind == RefKind.Ref && mixableArguments is not null) + { + mixableArguments.Add(new MixableDestination(argument, EscapeLevel.CallingMethod)); + } } } } @@ -1854,19 +2026,34 @@ private void GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, ImmutableArray argsToParamsOpt, - bool isRefEscape, - bool checkingMethodArgumentsMustMatch, + bool isInvokedWithRef, bool ignoreArglistRefKinds, - ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, bool IsRefEscape)> result) + ArrayBuilder escapeValues) { - if (!symbol.RequiresInstanceReceiver()) + // This code is attempting to implement the following portion of the spec. Essentially if we're not + // either invoking a method by ref or have a ref struct return then there is no need to consider the + // argument escape scopes when calculating the return escape scope. + // + // > A value resulting from a method invocation `e1.M(e2, ...)` is *safe-to-escape* from the narrowest of the following scopes: + // > 1. The *calling method* + // > 2. When the return is a `ref struct` the *safe-to-escape* contributed by all argument expressions + // > 3. When the return is a `ref struct` the *ref-safe-to-escape* contributed by all `ref` arguments + // + // The `ref` calling rules can be simplified to: + // + // > A value resulting from a method invocation `ref e1.M(e2, ...)` is *ref-safe-to-escape* the narrowest of the following scopes: + // > 1. The *calling method* + // > 2. The *safe-to-escape* contributed by all argument expressions + // > 3. The *ref-safe-to-escape* contributed by all `ref` arguments + + // If we're not invoking with ref or returning a ref struct then the spec does not consider + // any arguments hence the filter is always empty. + if (!isInvokedWithRef && !hasRefLikeReturn(symbol)) { - // ignore receiver when symbol is static - receiver = null; + return; } - var argsAndParams = ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, RefKind RefKind)>.GetInstance(); - GetInvocationArgumentsForEscape( + GetEscapeValuesForUpdatedRules( symbol, receiver, parameters, @@ -1874,73 +2061,101 @@ private void GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( argRefKindsOpt, argsToParamsOpt, ignoreArglistRefKinds, - argsAndParams); + mixableArguments: null, + escapeValues); - // https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md#rules-method-invocation - // - // A value resulting from a method invocation `e1.M(e2, ...)` is *safe-to-escape* from the smallest of the following scopes: - // 1. The *calling method* - // 2. The *safe-to-escape* contributed by all argument expressions - // 3. ... - // - // A value resulting from a method invocation `ref e1.M(e2, ...)` is *ref-safe-to-escape* the smallest of the following scopes: - // 1. The *safe-to-escape* of the rvalue of `e1.M(e2, ...)` - // 2. ... - foreach (var (parameter, argument, _) in argsAndParams) - { - // For a given argument `a` that is passed to parameter `p`: - // 1. ... - // 2. If `p` is `scoped` then `a` does not contribute *safe-to-escape* when considering arguments. - if (parameter?.EffectiveScope == DeclarationScope.ValueScoped) + static bool hasRefLikeReturn(Symbol symbol) + { + switch (symbol) { - continue; + case MethodSymbol method: + if (method.MethodKind == MethodKind.Constructor) + { + return method.ContainingType.IsRefLikeType; + } + + return method.ReturnType.IsRefLikeType; + case PropertySymbol property: + return property.Type.IsRefLikeType; + default: + return false; } - result.Add((parameter, argument, IsRefEscape: false)); } + } - // A value resulting from a method invocation `e1.M(e2, ...)` is *safe-to-escape* from the smallest of the following scopes: - // 1. ... - // 2. ... - // 3. When the return is a `ref struct` then *ref-safe-to-escape* contributed by all `ref` arguments - // - // A value resulting from a method invocation `ref e1.M(e2, ...)` is *ref-safe-to-escape* the smallest of the following scopes: - // 1. ... - // 2. The *ref-safe-to-escape* contributed by all `ref` arguments - if (isRefEscape || checkingMethodArgumentsMustMatch || hasRefStructType(symbol)) + /// + /// Returns the set of to an invocation that impact ref analysis. + /// This will filter out everything that could never meaningfully contribute to ref analysis. For + /// example: + /// - For ref arguments it will return an for both ref and + /// value escape (if appropriate based on scoped-ness of associated parameters). + /// - It will remove value escape for args which correspond to scoped parameters. + /// - It will remove value escape for non-ref struct. + /// - It will remove ref escape for args which correspond to scoped refs. + /// Optionally this will also return all of the that + /// result from this invocation. That is useful for MAMM analysis. + /// + private void GetEscapeValuesForUpdatedRules( + Symbol symbol, + BoundExpression? receiver, + ImmutableArray parameters, + ImmutableArray argsOpt, + ImmutableArray argRefKindsOpt, + ImmutableArray argsToParamsOpt, + bool ignoreArglistRefKinds, + ArrayBuilder? mixableArguments, + ArrayBuilder escapeValues) + { + if (!symbol.RequiresInstanceReceiver()) + { + // ignore receiver when symbol is static + receiver = null; + } + + var escapeArguments = ArrayBuilder.GetInstance(); + GetInvocationArgumentsForEscape( + symbol, + receiver, + parameters, + argsOpt, + argRefKindsOpt, + argsToParamsOpt, + ignoreArglistRefKinds, + mixableArguments, + escapeArguments); + + foreach (var (parameter, argument, refKind) in escapeArguments) { - foreach (var (parameter, argument, effectiveRefKind) in argsAndParams) + // This means it's part of an __arglist or function pointer receiver. + if (parameter is null) { - if (effectiveRefKind == RefKind.None) + if (refKind != RefKind.None) { - continue; + escapeValues.Add(new EscapeValue(parameter: null, argument, EscapeLevel.ReturnOnly, isRefEscape: true)); } - // For a given argument `a` that is passed to parameter `p`: - // 1. If `p` is `scoped ref` then `a` does not contribute *ref-safe-to-escape* when considering arguments. - // 2. ... - if (parameter?.EffectiveScope == DeclarationScope.RefScoped || (checkingMethodArgumentsMustMatch && parameter?.Type.IsRefLikeType == true)) + + if (argument.Type?.IsRefLikeType == true) { - continue; + escapeValues.Add(new EscapeValue(parameter: null, argument, EscapeLevel.CallingMethod, isRefEscape: false)); } - result.Add((parameter, argument, IsRefEscape: true)); + + continue; } - } - argsAndParams.Free(); + if (parameter.Type.IsRefLikeType && GetParameterValEscapeLevel(parameter) is { } valEscapeLevel) + { + escapeValues.Add(new EscapeValue(parameter, argument, valEscapeLevel, isRefEscape: false)); + } - static bool hasRefStructType(Symbol symbol) - { - switch (symbol) + // It's important to check values then references. Flipping will change the set of errors + // produced by MAMM because of the CheckRefEscape / CheckValEscape calls. + if (parameter.RefKind != RefKind.None && GetParameterRefEscapeLevel(parameter) is { } refEscapeLevel) { - case MethodSymbol method: - return method.MethodKind == MethodKind.Constructor ? - method.ContainingType.IsRefLikeType : - method.ReturnType.IsRefLikeType; - case PropertySymbol property: - return property.Type.IsRefLikeType; - default: - return false; + escapeValues.Add(new EscapeValue(parameter, argument, refEscapeLevel, isRefEscape: true)); } } + + escapeArguments.Free(); } private static string GetInvocationParameterName(ParameterSymbol? parameter) @@ -2021,7 +2236,7 @@ private bool CheckInvocationArgMixing( escapeTo = GetValEscape(receiverOpt, scopeOfTheContainingExpression); } - var argsAndParams = ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, RefKind RefKind)>.GetInstance(); + var escapeArguments = ArrayBuilder.GetInstance(); GetInvocationArgumentsForEscape( symbol, receiver: null, // receiver handled explicitly below @@ -2030,11 +2245,12 @@ private bool CheckInvocationArgMixing( argRefKindsOpt: default, argsToParamsOpt, ignoreArglistRefKinds: false, - argsAndParams); + mixableArguments: null, + escapeArguments); try { - foreach (var (_, argument, refKind) in argsAndParams) + foreach (var (_, argument, refKind) in escapeArguments) { if (refKind.IsWritableReference() && argument.Type?.IsRefLikeType == true) { @@ -2048,7 +2264,7 @@ private bool CheckInvocationArgMixing( return true; } - foreach (var (parameter, argument, _) in argsAndParams) + foreach (var (parameter, argument, _) in escapeArguments) { var valid = CheckValEscape(argument.Syntax, argument, scopeOfTheContainingExpression, escapeTo, false, diagnostics); @@ -2062,7 +2278,7 @@ private bool CheckInvocationArgMixing( } finally { - argsAndParams.Free(); + escapeArguments.Free(); } // check val escape of receiver if ref-like @@ -2086,103 +2302,59 @@ private bool CheckInvocationArgMixingWithUpdatedRules( uint scopeOfTheContainingExpression, BindingDiagnosticBag diagnostics) { - // https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md#rules-method-invocation - // - // For any method invocation `e.M(a1, a2, ... aN)` - // 1. Calculate the *safe-to-escape* of the method return. - // - Ignore the *ref-safe-to-escape* of arguments to `in / ref / out` parameters of `ref struct` types. The corresponding parameters *ref-safe-to-escape* are at most *return only* and hence cannot be returned via `ref` or `out` parameters. - // - Assume the method has a `ref struct` return type. - // 2. All `ref` or `out` arguments of `ref struct` types must be assignable by a value with that *safe-to-escape*. - // This applies even when the `ref` argument matches a `scoped ref` parameter. - - if (!symbol.RequiresInstanceReceiver()) - { - // ignore receiver when symbol is static - receiverOpt = null; - } - - // 1. Calculate the *safe-to-escape* of the method return. - // - Ignore the *ref-safe-to-escape* of arguments to `in / ref / out` parameters of `ref struct` types. The corresponding parameters *ref-safe-to-escape* are at most *return only* and hence cannot be returned via `ref` or `out` parameters. - // - Assume the method has a `ref struct` return type. - uint escapeScope = Binder.CallingMethodScope; - (ParameterSymbol? Parameter, BoundExpression Argument, bool IsRefEscape)? argAndParamToEscape = null; - var argsAndParamsAll = ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, bool IsRefEscape)>.GetInstance(); - GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( + var mixableArguments = ArrayBuilder.GetInstance(); + var escapeValues = ArrayBuilder.GetInstance(); + GetEscapeValuesForUpdatedRules( symbol, receiverOpt, parameters, argsOpt, argRefKindsOpt, argsToParamsOpt, - isRefEscape: false, - checkingMethodArgumentsMustMatch: true, ignoreArglistRefKinds: false, - argsAndParamsAll); - foreach (var argAndParam in argsAndParamsAll) - { - var argument = argAndParam.Argument; - uint argEscape = argAndParam.IsRefEscape ? - GetRefEscape(argument, scopeOfTheContainingExpression) : - GetValEscape(argument, scopeOfTheContainingExpression); - - if (argEscape > escapeScope) - { - escapeScope = argEscape; - argAndParamToEscape = argAndParam; - } - } - argsAndParamsAll.Free(); + mixableArguments, + escapeValues); - if (argAndParamToEscape is null) + var valid = true; + foreach (var mixableArg in mixableArguments) { - return true; - } - - var argsAndParams = ArrayBuilder<(ParameterSymbol? Parameter, BoundExpression Argument, RefKind RefKind)>.GetInstance(); - GetInvocationArgumentsForEscape( - symbol, - receiverOpt, - parameters, - argsOpt, - argRefKindsOpt: default, - argsToParamsOpt, - ignoreArglistRefKinds: false, - argsAndParams); - - // 2. All `ref` or `out` arguments of `ref struct` types must be assignable by a value with that *safe-to-escape*. - // This applies even when the `ref` argument matches a `scoped ref` parameter. - bool result = true; - foreach (var (_, argument, refKind) in argsAndParams) - { - if (!refKind.IsWritableReference() || argument.Type?.IsRefLikeType != true) + var toArgEscape = GetValEscape(mixableArg.Argument, scopeOfTheContainingExpression); + foreach (var (fromParameter, fromArg, escapeKind, isRefEscape) in escapeValues) { - continue; - } + if (mixableArg.Parameter is not null && object.ReferenceEquals(mixableArg.Parameter, fromParameter)) + { + continue; + } - uint escapeTo = GetValEscape(argument, scopeOfTheContainingExpression); - var (parameterToEscape, argToEscape, isRefEscape) = argAndParamToEscape.GetValueOrDefault(); + // This checks to see if the EscapeValue could ever be assigned to this argument based + // on comparing the EscapeLevel of both. If this could never be assigned due to + // this then we don't need to consider it for MAMM analysis. + if (!mixableArg.IsAssignableFrom(escapeKind)) + { + continue; + } - bool valid = isRefEscape ? - CheckRefEscape(argToEscape.Syntax, argToEscape, scopeOfTheContainingExpression, escapeTo, false, diagnostics) : - CheckValEscape(argToEscape.Syntax, argToEscape, scopeOfTheContainingExpression, escapeTo, false, diagnostics); + valid = isRefEscape + ? CheckRefEscape(fromArg.Syntax, fromArg, scopeOfTheContainingExpression, toArgEscape, checkingReceiver: false, diagnostics) + : CheckValEscape(fromArg.Syntax, fromArg, scopeOfTheContainingExpression, toArgEscape, checkingReceiver: false, diagnostics); - if (!valid) - { - // For consistency with C#10 implementation, we don't report ERR_CallArgMixing - // for the receiver. (In both implementations, the call to CheckValEscape() above - // will have reported a specific escape error for the receiver though.) - if ((object)argToEscape != receiverOpt) + if (!valid) { - string parameterName = GetInvocationParameterName(parameterToEscape); + string parameterName = GetInvocationParameterName(fromParameter); Error(diagnostics, ErrorCode.ERR_CallArgMixing, syntax, symbol, parameterName); + break; } - result = false; + } + + if (!valid) + { break; } } - argsAndParams.Free(); - return result; + mixableArguments.Free(); + escapeValues.Free(); + return valid; } private static bool IsReceiverRefReadOnly(Symbol methodOrPropertySymbol) => methodOrPropertySymbol switch @@ -3046,9 +3218,12 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres // otherwise default to ExternalScope (ordinary values) switch (expr.Kind) { + case BoundKind.ThisReference: + var thisParam = ((MethodSymbol)this.ContainingMember()).ThisParameter; + Debug.Assert(thisParam.Type.Equals(((BoundThisReference)expr).Type, TypeCompareKind.ConsiderEverything)); + return GetParameterValEscape(thisParam); case BoundKind.DefaultLiteral: case BoundKind.DefaultExpression: - case BoundKind.ThisReference: case BoundKind.Utf8String: // always returnable return Binder.CallingMethodScope; @@ -3459,15 +3634,19 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF switch (expr.Kind) { + case BoundKind.ThisReference: + var thisParam = ((MethodSymbol)this.ContainingMember()).ThisParameter; + Debug.Assert(thisParam.Type.Equals(((BoundThisReference)expr).Type, TypeCompareKind.ConsiderEverything)); + return CheckParameterValEscape(node, thisParam, escapeTo, diagnostics); + case BoundKind.DefaultLiteral: case BoundKind.DefaultExpression: - case BoundKind.ThisReference: case BoundKind.Utf8String: // always returnable return true; case BoundKind.Parameter: - return CheckParameterValEscape(node, (BoundParameter)expr, escapeTo, diagnostics); + return CheckParameterValEscape(node, ((BoundParameter)expr).ParameterSymbol, escapeTo, diagnostics); case BoundKind.TupleLiteral: case BoundKind.ConvertedTupleLiteral: diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 2ef72385eadf3..350d74c1d5b2a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -13817,6 +13817,9 @@ public ref struct S1 comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.NetCoreApp); comp.VerifyDiagnostics( + // (10,100): error CS8352: Cannot use variable 'out CustomHandler' in this context because it may expose referenced variables outside of their declaration scope + // public CustomHandler(int literalLength, int formattedCount, ref S1 s1) : this() { s1.Handler = this; } + Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler").WithLocation(10, 100), // (15,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"{s2}"); Diagnostic(ErrorCode.ERR_CallArgMixing, @"M2(ref s1, $""{s2}"")").WithArguments("CustomHandler.M2(ref S1, CustomHandler)", "handler").WithLocation(15, 9), @@ -13862,6 +13865,9 @@ static void M(ref S s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.NetCoreApp); comp.VerifyDiagnostics( + // (5,97): error CS8352: Cannot use variable 'out CustomHandler' in this context because it may expose referenced variables outside of their declaration scope + // public CustomHandler(int literalLength, int formattedCount, ref S s) : this() { s.Handler = this; } + Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler").WithLocation(5, 97), // (17,15): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // M(ref s, $"{1}"); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "s").WithLocation(17, 15), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs index 46138bacd21be..3be739ff34b48 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs @@ -11278,6 +11278,9 @@ public ref struct S1 var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); comp.VerifyDiagnostics( + // (10,107): error CS8352: Cannot use variable 'out CustomHandler' in this context because it may expose referenced variables outside of their declaration scope + // public CustomHandler(int literalLength, int formattedCount, scoped ref S1 s1) : this() { s1.Handler = this; } + Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler").WithLocation(10, 107), // (15,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"""{s2}"""); Diagnostic(ErrorCode.ERR_CallArgMixing, @"M2(ref s1, $""""""{s2}"""""")").WithArguments("CustomHandler.M2(ref S1, CustomHandler)", "handler").WithLocation(15, 9), @@ -11312,7 +11315,10 @@ static void M(ref S s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler }"; var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (5,104): error CS8352: Cannot use variable 'out CustomHandler' in this context because it may expose referenced variables outside of their declaration scope + // public CustomHandler(int literalLength, int formattedCount, scoped ref S s) : this() { s.Handler = this; } + Diagnostic(ErrorCode.ERR_EscapeVariable, "this").WithArguments("out CustomHandler").WithLocation(5, 104)); } [Theory, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index 609d0c623dc40..1e8f361c744dc 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -1703,15 +1703,8 @@ ref struct S1 Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign1(__arglist(ref inner, ref rOuter))").WithArguments("Program.MayAssign1(__arglist)", "__arglist").WithLocation(23, 9) ); - // Breaking change in C#11: A ref to ref struct argument is considered - // an unscoped reference when passed to an __arglist. + // Same errors modulo the scoped CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics( - // (17,34): error CS8168: Cannot return local 'rOuter' by reference because it is not a ref local - // MayAssign2(__arglist(ref rOuter, ref rOuter)); - Diagnostic(ErrorCode.ERR_RefReturnLocal, "rOuter").WithArguments("rOuter").WithLocation(17, 34), - // (17,9): error CS8350: This combination of arguments to 'Program.MayAssign2(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // MayAssign2(__arglist(ref rOuter, ref rOuter)); - Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign2(__arglist(ref rOuter, ref rOuter))").WithArguments("Program.MayAssign2(__arglist)", "__arglist").WithLocation(17, 9), // (20,46): error CS8352: Cannot use variable 'rInner' in this context because it may expose referenced variables outside of their declaration scope // MayAssign2(__arglist(ref rOuter, ref rInner)); Diagnostic(ErrorCode.ERR_EscapeVariable, "rInner").WithArguments("rInner").WithLocation(20, 46), @@ -2878,13 +2871,13 @@ ref struct S1 "; var comp = CreateCompilationWithMscorlibAndSpan(text, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); comp.VerifyDiagnostics( - // (16,30): error CS8526: Cannot use variable 'local' in this context because it may expose referenced variables outside of their declaration scope + // (16,30): error CS8352: Cannot use variable 'local' in this context because it may expose referenced variables outside of their declaration scope // sp = MayWrap(ref local); Diagnostic(ErrorCode.ERR_EscapeVariable, "local").WithArguments("local").WithLocation(16, 30), - // (16,18): error CS8521: Cannot use a result of 'Program.MayWrap(ref Span)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope + // (16,18): error CS8347: Cannot use a result of 'Program.MayWrap(ref Span)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope // sp = MayWrap(ref local); Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref local)").WithArguments("Program.MayWrap(ref System.Span)", "arg").WithLocation(16, 18), - // (22,20): error CS8526: Cannot use variable 'sp1' in this context because it may expose referenced variables outside of their declaration scope + // (22,20): error CS8352: Cannot use variable 'sp1' in this context because it may expose referenced variables outside of their declaration scope // return sp1; Diagnostic(ErrorCode.ERR_EscapeVariable, "sp1").WithArguments("sp1").WithLocation(22, 20) ); @@ -2949,6 +2942,7 @@ public void MemberOfReadonlyRefLikeEscape(LanguageVersion languageVersion) { var text = @" using System; + using System.Diagnostics.CodeAnalysis; public static class Program { public static void Main() @@ -2957,8 +2951,13 @@ public static void Main() Span value1 = stackalloc int[1]; new SR().TryGet(out value1); - // error, TryGet can write into the instance + // Ok, the new value can be copied into SW but not the + // ref to the value new SW().TryGet(out value1); + + // Error as the ref of this can escape into value2 + Span value2 = default; + new SW().TryGet2(out value2); } } @@ -2976,16 +2975,37 @@ public void TryGet(out Span result) { result = default; } + + [UnscopedRef] + public void TryGet2(out Span result) + { + result = default; + } } "; - CreateCompilationWithMscorlibAndSpan(text, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)).VerifyDiagnostics( - // (12,33): error CS8526: Cannot use variable 'value1' in this context because it may expose referenced variables outside of their declaration scope - // new SW().TryGet(out value1); - Diagnostic(ErrorCode.ERR_EscapeVariable, "value1").WithArguments("value1").WithLocation(12, 33), - // (12,13): error CS8524: This combination of arguments to 'SW.TryGet(out Span)' is disallowed because it may expose variables referenced by parameter 'result' outside of their declaration scope - // new SW().TryGet(out value1); - Diagnostic(ErrorCode.ERR_CallArgMixing, "new SW().TryGet(out value1)").WithArguments("SW.TryGet(out System.Span)", "result").WithLocation(12, 13) - ); + if (languageVersion == LanguageVersion.CSharp10) + { + CreateCompilationWithMscorlibAndSpan(new[] { text, UnscopedRefAttributeDefinition }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)).VerifyDiagnostics( + // (14,13): error CS8350: This combination of arguments to 'SW.TryGet(out Span)' is disallowed because it may expose variables referenced by parameter 'result' outside of their declaration scope + // new SW().TryGet(out value1); + Diagnostic(ErrorCode.ERR_CallArgMixing, "new SW().TryGet(out value1)").WithArguments("SW.TryGet(out System.Span)", "result").WithLocation(14, 13), + // (14,33): error CS8352: Cannot use variable 'value1' in this context because it may expose referenced variables outside of their declaration scope + // new SW().TryGet(out value1); + Diagnostic(ErrorCode.ERR_EscapeVariable, "value1").WithArguments("value1").WithLocation(14, 33) + ); + + } + else + { + CreateCompilationWithMscorlibAndSpan(new[] { text, UnscopedRefAttributeDefinition }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)).VerifyDiagnostics( + // (18,13): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // new SW().TryGet2(out value2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "new SW()").WithLocation(18, 13), + // (18,13): error CS8350: This combination of arguments to 'SW.TryGet2(out Span)' is disallowed because it may expose variables referenced by parameter 'this' outside of their declaration scope + // new SW().TryGet2(out value2); + Diagnostic(ErrorCode.ERR_CallArgMixing, "new SW().TryGet2(out value2)").WithArguments("SW.TryGet2(out System.Span)", "this").WithLocation(18, 13) + ); + } } [WorkItem(21911, "https://github.com/dotnet/roslyn/issues/21911")] @@ -3503,17 +3523,28 @@ public static class Extensions public static void Deconstruct(ref this Span self, out Span x, out Span y) => throw null; } "; - CreateCompilationWithMscorlibAndSpan(text, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)).VerifyDiagnostics( - // (8,9): error CS1510: A ref or out value must be an assignable variable - // (global, global) = global; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "(global, global) = global").WithLocation(8, 9), - // (8,9): error CS8352: Cannot use variable '(global, global) = global' in this context because it may expose referenced variables outside of their declaration scope - // (global, global) = global; - Diagnostic(ErrorCode.ERR_EscapeVariable, "(global, global) = global").WithArguments("(global, global) = global").WithLocation(8, 9), - // (8,28): error CS8350: This combination of arguments to 'Extensions.Deconstruct(ref Span, out Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope - // (global, global) = global; - Diagnostic(ErrorCode.ERR_CallArgMixing, "global").WithArguments("Extensions.Deconstruct(ref System.Span, out System.Span, out System.Span)", "x").WithLocation(8, 28) - ); + if (languageVersion == LanguageVersion.CSharp10) + { + CreateCompilationWithMscorlibAndSpan(text, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)).VerifyDiagnostics( + // (8,9): error CS1510: A ref or out value must be an assignable variable + // (global, global) = global; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "(global, global) = global").WithLocation(8, 9), + // (8,9): error CS8352: Cannot use variable '(global, global) = global' in this context because it may expose referenced variables outside of their declaration scope + // (global, global) = global; + Diagnostic(ErrorCode.ERR_EscapeVariable, "(global, global) = global").WithArguments("(global, global) = global").WithLocation(8, 9), + // (8,28): error CS8350: This combination of arguments to 'Extensions.Deconstruct(ref Span, out Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (global, global) = global; + Diagnostic(ErrorCode.ERR_CallArgMixing, "global").WithArguments("Extensions.Deconstruct(ref System.Span, out System.Span, out System.Span)", "x").WithLocation(8, 28) + ); + } + else + { + CreateCompilationWithMscorlibAndSpan(text, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)).VerifyDiagnostics( + // (8,9): error CS1510: A ref or out value must be an assignable variable + // (global, global) = global; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "(global, global) = global").WithLocation(8, 9) + ); + } } [Fact] @@ -3550,10 +3581,7 @@ public static class Extensions CreateCompilationWithMscorlibAndSpan(text, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.Regular11).VerifyDiagnostics( // (8,9): error CS1510: A ref or out value must be an assignable variable // (global, global) = global; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "(global, global) = global").WithLocation(8, 9), - // (8,9): warning CS9077: Use of variable '(global, global) = global' in this context may expose referenced variables outside of their declaration scope - // (global, global) = global; - Diagnostic(ErrorCode.WRN_EscapeVariable, "(global, global) = global").WithArguments("(global, global) = global").WithLocation(8, 9) + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "(global, global) = global").WithLocation(8, 9) ); } @@ -4080,11 +4108,25 @@ public ref struct S "; // Tracking issue: https://github.com/dotnet/roslyn/issues/22361 - CreateCompilationWithMscorlibAndSpan(text, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)).VerifyDiagnostics( - // (9,9): error CS8352: Cannot use variable 'local1' in this context because it may expose referenced variables outside of their declaration scope - // local1.M(out S local2); - Diagnostic(ErrorCode.ERR_EscapeVariable, "local1").WithArguments("local1").WithLocation(9, 9) - ); + if (languageVersion == LanguageVersion.CSharp10) + { + CreateCompilationWithMscorlibAndSpan(text, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)).VerifyDiagnostics( + // (9,9): error CS8352: Cannot use variable 'local1' in this context because it may expose referenced variables outside of their declaration scope + // local1.M(out S local2); + Diagnostic(ErrorCode.ERR_EscapeVariable, "local1").WithArguments("local1").WithLocation(9, 9) + ); + } + else + { + CreateCompilationWithMscorlibAndSpan(text, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)).VerifyDiagnostics( + // (9,9): error CS8352: Cannot use variable 'local1' in this context because it may expose referenced variables outside of their declaration scope + // local1.M(out S local2); // we'd want this to succeed, but determine the safe-to-escape scope for local2 based on the invocation that declared it + Diagnostic(ErrorCode.ERR_EscapeVariable, "local1").WithArguments("local1").WithLocation(9, 9), + // (9,9): error CS8350: This combination of arguments to 'S.M(out S)' is disallowed because it may expose variables referenced by parameter 'this' outside of their declaration scope + // local1.M(out S local2); // we'd want this to succeed, but determine the safe-to-escape scope for local2 based on the invocation that declared it + Diagnostic(ErrorCode.ERR_CallArgMixing, "local1.M(out S local2)").WithArguments("S.M(out S)", "this").WithLocation(9, 9) + ); + } } [Theory] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 22e7a208a720f..420dc442e4148 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -68,7 +68,7 @@ static void M1(T t1) } static void M2(ref T t2) { - S s2; + scoped S s2; s2 = new S(ref t2); s2 = new S { F1 = t2 }; } @@ -111,7 +111,10 @@ void M5(S s5) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref T").WithArguments("ref fields", "11.0").WithLocation(3, 12), // (4,12): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // public ref readonly T F2; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref readonly T").WithArguments("ref fields", "11.0").WithLocation(4, 12)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref readonly T").WithArguments("ref fields", "11.0").WithLocation(4, 12), + // (25,9): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // scoped S s2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(25, 9)); comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithRefFields }); comp.VerifyEmitDiagnostics(); @@ -129,7 +132,7 @@ static void M1(T t) } static void M2(ref T t) { - S s2; + scoped S s2; s2 = new S(ref t); s2 = new S { F1 = t }; } @@ -147,6 +150,9 @@ static void M3(S s) // (8,25): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // s1 = new S { F1 = t }; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "F1").WithArguments("ref fields", "11.0").WithLocation(8, 25), + // (12,9): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // scoped S s2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(12, 9), // (14,25): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // s2 = new S { F1 = t }; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "F1").WithArguments("ref fields", "11.0").WithLocation(14, 25), @@ -2107,6 +2113,12 @@ static R F2(R r2) // (3,5): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // ref T F; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref T").WithArguments("ref fields", "11.0").WithLocation(3, 5), + // (7,9): error CS9079: Cannot ref-assign 't' to 'F' because 't' can only escape the current method through a return statement. + // F = ref t; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "F = ref t").WithArguments("F", "t").WithLocation(7, 9), + // (12,9): error CS9079: Cannot ref-assign 't' to 'F' because 't' can only escape the current method through a return statement. + // r1.F = ref t; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "r1.F = ref t").WithArguments("F", "t").WithLocation(12, 9), // (18,9): error CS8374: Cannot ref-assign 't' to 'F' because 't' has a narrower escape scope than 'F'. // r2.F = ref t; Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r2.F = ref t").WithArguments("F", "t").WithLocation(18, 9) @@ -2114,6 +2126,12 @@ static R F2(R r2) comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); comp.VerifyEmitDiagnostics( + // (7,9): error CS9079: Cannot ref-assign 't' to 'F' because 't' can only escape the current method through a return statement. + // F = ref t; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "F = ref t").WithArguments("F", "t").WithLocation(7, 9), + // (12,9): error CS9079: Cannot ref-assign 't' to 'F' because 't' can only escape the current method through a return statement. + // r1.F = ref t; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "r1.F = ref t").WithArguments("F", "t").WithLocation(12, 9), // (18,9): error CS8374: Cannot ref-assign 't' to 'F' because 't' has a narrower escape scope than 'F'. // r2.F = ref t; Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r2.F = ref t").WithArguments("F", "t").WithLocation(18, 9) @@ -2276,7 +2294,7 @@ static void Main() static void Test(ref int i) { - var r = new R(); + scoped var r = new R(); r.Field = ref i; Console.WriteLine(r.Field); } @@ -3838,50 +3856,14 @@ static void F1(ref R a, __arglist) { } static void F51(scoped ref R x, scoped ref R y) { F1(ref x, __arglist(ref y)); } // 6 }"; var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (7,41): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F00(ref R x, ref R y) { F0(__arglist(ref x, ref y)); } // 1 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x, ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(7, 41), - // (7,58): error CS9077: Cannot return a parameter by reference 'x' through a ref parameter; it can only be returned in a return statement - // static void F00(ref R x, ref R y) { F0(__arglist(ref x, ref y)); } // 1 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "x").WithArguments("x").WithLocation(7, 58), - // (8,41): error CS8350: This combination of arguments to 'Program.F1(ref R, __arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F01(ref R x, ref R y) { F1(ref x, __arglist(ref y)); } // 2 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x, __arglist(ref y))").WithArguments("Program.F1(ref R, __arglist)", "__arglist").WithLocation(8, 41), - // (8,65): error CS9077: Cannot return a parameter by reference 'y' through a ref parameter; it can only be returned in a return statement - // static void F01(ref R x, ref R y) { F1(ref x, __arglist(ref y)); } // 2 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "y").WithArguments("y").WithLocation(8, 65), - // (9,48): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F20(ref R x, scoped ref R y) { F0(__arglist(ref x, ref y)); } // 3 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x, ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(9, 48), - // (9,72): error CS9075: Cannot return a parameter by reference 'y' because it is scoped to the current method - // static void F20(ref R x, scoped ref R y) { F0(__arglist(ref x, ref y)); } // 3 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "y").WithArguments("y").WithLocation(9, 72), - // (10,48): error CS8350: This combination of arguments to 'Program.F1(ref R, __arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F21(ref R x, scoped ref R y) { F1(ref x, __arglist(ref y)); } // 4 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x, __arglist(ref y))").WithArguments("Program.F1(ref R, __arglist)", "__arglist").WithLocation(10, 48), - // (10,72): error CS9075: Cannot return a parameter by reference 'y' because it is scoped to the current method - // static void F21(ref R x, scoped ref R y) { F1(ref x, __arglist(ref y)); } // 4 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "y").WithArguments("y").WithLocation(10, 72), - // (11,55): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F50(scoped ref R x, scoped ref R y) { F0(__arglist(ref x, ref y)); } // 5 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x, ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(11, 55), - // (11,72): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method - // static void F50(scoped ref R x, scoped ref R y) { F0(__arglist(ref x, ref y)); } // 5 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(11, 72), - // (12,55): error CS8350: This combination of arguments to 'Program.F1(ref R, __arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F51(scoped ref R x, scoped ref R y) { F1(ref x, __arglist(ref y)); } // 6 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x, __arglist(ref y))").WithArguments("Program.F1(ref R, __arglist)", "__arglist").WithLocation(12, 55), - // (12,79): error CS9075: Cannot return a parameter by reference 'y' because it is scoped to the current method - // static void F51(scoped ref R x, scoped ref R y) { F1(ref x, __arglist(ref y)); } // 6 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "y").WithArguments("y").WithLocation(12, 79)); + comp.VerifyEmitDiagnostics(); } [Fact] public void MethodArgumentsMustMatch_07_1() { var source = -@"using System.Diagnostics.CodeAnalysis; +@" ref struct R { } class Program { @@ -3896,43 +3878,7 @@ static void F1(scoped ref R a, __arglist) { } static void F51(ref R x, ref R y) { F1(ref x, __arglist(ref y)); } // 6 }"; var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); - comp.VerifyEmitDiagnostics( - // (8,72): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method - // static void F00(scoped ref R x, scoped ref R y) { F0(__arglist(ref x, ref y)); } // 1 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(8, 72), - // (8,55): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F00(scoped ref R x, scoped ref R y) { F0(__arglist(ref x, ref y)); } // 1 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x, ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(8, 55), - // (9,79): error CS9075: Cannot return a parameter by reference 'y' because it is scoped to the current method - // static void F01(scoped ref R x, scoped ref R y) { F1(ref x, __arglist(ref y)); } // 2 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "y").WithArguments("y").WithLocation(9, 79), - // (9,55): error CS8350: This combination of arguments to 'Program.F1(ref R, __arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F01(scoped ref R x, scoped ref R y) { F1(ref x, __arglist(ref y)); } // 2 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x, __arglist(ref y))").WithArguments("Program.F1(ref R, __arglist)", "__arglist").WithLocation(9, 55), - // (10,65): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method - // static void F20(scoped ref R x, ref R y) { F0(__arglist(ref x, ref y)); } // 3 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(10, 65), - // (10,48): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F20(scoped ref R x, ref R y) { F0(__arglist(ref x, ref y)); } // 3 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x, ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(10, 48), - // (11,72): error CS9077: Cannot return a parameter by reference 'y' through a ref parameter; it can only be returned in a return statement - // static void F21(scoped ref R x, ref R y) { F1(ref x, __arglist(ref y)); } // 4 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "y").WithArguments("y").WithLocation(11, 72), - // (11,48): error CS8350: This combination of arguments to 'Program.F1(ref R, __arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F21(scoped ref R x, ref R y) { F1(ref x, __arglist(ref y)); } // 4 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x, __arglist(ref y))").WithArguments("Program.F1(ref R, __arglist)", "__arglist").WithLocation(11, 48), - // (12,58): error CS9077: Cannot return a parameter by reference 'x' through a ref parameter; it can only be returned in a return statement - // static void F50(ref R x, ref R y) { F0(__arglist(ref x, ref y)); } // 5 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "x").WithArguments("x").WithLocation(12, 58), - // (12,41): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F50(ref R x, ref R y) { F0(__arglist(ref x, ref y)); } // 5 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x, ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(12, 41), - // (13,65): error CS9077: Cannot return a parameter by reference 'y' through a ref parameter; it can only be returned in a return statement - // static void F51(ref R x, ref R y) { F1(ref x, __arglist(ref y)); } // 6 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "y").WithArguments("y").WithLocation(13, 65), - // (13,41): error CS8350: This combination of arguments to 'Program.F1(ref R, __arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F51(ref R x, ref R y) { F1(ref x, __arglist(ref y)); } // 6 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x, __arglist(ref y))").WithArguments("Program.F1(ref R, __arglist)", "__arglist").WithLocation(13, 41)); + comp.VerifyEmitDiagnostics(); } [Fact] @@ -3973,31 +3919,7 @@ static R F0(__arglist) comp.VerifyEmitDiagnostics( // (17,9): error CS8374: Cannot ref-assign 'r.B' to 'RB' because 'r.B' has a narrower escape scope than 'RB'. // r.RB = ref r.B; // 1 - Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r.RB = ref r.B").WithArguments("RB", "r.B").WithLocation(17, 9), - // (21,55): error CS9075: Cannot return a parameter by reference 'y' because it is scoped to the current method - // static void F1(scoped ref R y) { F0(__arglist(ref y)); } // 2 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "y").WithArguments("y").WithLocation(21, 55), - // (21,38): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F1(scoped ref R y) { F0(__arglist(ref y)); } // 2 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(21, 38), - // (22,48): error CS9077: Cannot return a parameter by reference 'y' through a ref parameter; it can only be returned in a return statement - // static void F2(ref R y) { F0(__arglist(ref y)); } // 3 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "y").WithArguments("y").WithLocation(22, 48), - // (22,31): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static void F2(ref R y) { F0(__arglist(ref y)); } // 3 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(22, 31), - // (24,59): error CS9075: Cannot return a parameter by reference 'y' because it is scoped to the current method - // static R F3(scoped ref R y) { return F0(__arglist(ref y)); } // 4 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "y").WithArguments("y").WithLocation(24, 59), - // (24,42): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static R F3(scoped ref R y) { return F0(__arglist(ref y)); } // 4 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(24, 42), - // (25,52): error CS9077: Cannot return a parameter by reference 'y' through a ref parameter; it can only be returned in a return statement - // static R F4(ref R y) { return F0(__arglist(ref y)); } // 5 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "y").WithArguments("y").WithLocation(25, 52), - // (25,35): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // static R F4(ref R y) { return F0(__arglist(ref y)); } // 5 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(25, 35)); + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r.RB = ref r.B").WithArguments("RB", "r.B").WithLocation(17, 9)); } [Fact] @@ -4063,18 +3985,6 @@ static void F1() comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (13,9): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // F0(__arglist(ref x)); // 1 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(13, 9), - // (13,26): error CS8168: Cannot return local 'x' by reference because it is not a ref local - // F0(__arglist(ref x)); // 1 - Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(13, 26), - // (15,9): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // F0(__arglist(ref x, ref x)); // 2 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x, ref x))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(15, 9), - // (15,26): error CS8168: Cannot return local 'x' by reference because it is not a ref local - // F0(__arglist(ref x, ref x)); // 2 - Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(15, 26), // (16,9): error CS8350: This combination of arguments to 'Program.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope // F0(__arglist(ref x, ref y)); // 3 Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x, ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(16, 9), @@ -4120,12 +4030,6 @@ static void F1() comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (13,9): error CS8350: This combination of arguments to 'Program.F0(ref R, __arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // F0(ref x, __arglist(ref x)); // 1 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(ref x, __arglist(ref x))").WithArguments("Program.F0(ref R, __arglist)", "__arglist").WithLocation(13, 9), - // (13,33): error CS8168: Cannot return local 'x' by reference because it is not a ref local - // F0(ref x, __arglist(ref x)); // 1 - Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(13, 33), // (14,9): error CS8350: This combination of arguments to 'Program.F0(ref R, __arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope // F0(ref x, __arglist(ref y)); // 2 Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(ref x, __arglist(ref y))").WithArguments("Program.F0(ref R, __arglist)", "__arglist").WithLocation(14, 9), @@ -4232,6 +4136,46 @@ static void F2(ref R x2, ref R y2) Diagnostic(ErrorCode.ERR_EscapeVariable, "y1").WithArguments("y1").WithLocation(13, 16)); } + /// + /// Ensure that readonly members / types are properly accounted for + /// + [Fact] + public void MethodArgumentsMustMatch_13() + { + var source = """ + ref struct RWRS + { + public void M1(RS rs) { } + public readonly void M2(RS rs) { } + } + readonly ref struct RORS + { + public void M3(RS rs) { } + } + ref struct RS + { + static void Test() + { + scoped RS local = default; + RWRS rwLocal = default; + rwLocal.M1(local); // 1 + rwLocal.M2(local); + RORS roLocal = default; + roLocal.M3(local); + } + } + """; + + var comp = CreateCompilation(new[] { source }); + comp.VerifyDiagnostics( + // (16,9): error CS8350: This combination of arguments to 'RWRS.M1(RS)' is disallowed because it may expose variables referenced by parameter 'rs' outside of their declaration scope + // rwLocal.M1(local); // 1 + Diagnostic(ErrorCode.ERR_CallArgMixing, "rwLocal.M1(local)").WithArguments("RWRS.M1(RS)", "rs").WithLocation(16, 9), + // (16,20): error CS8352: Cannot use variable 'local' in this context because it may expose referenced variables outside of their declaration scope + // rwLocal.M1(local); // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "local").WithArguments("local").WithLocation(16, 20)); + } + [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] @@ -4336,20 +4280,20 @@ static R F1() { var r1 = new R(); int i = 1; - r1.F(in i); // 1 + r1.F(in i); return r1; } static R F2() { var r2 = new R(); int i = 2; - r2.F(i); // 2 + r2.F(i); return r2; } static R F3() { var r3 = new R(); - r3.F(3); // 3 + r3.F(3); return r3; } }"; @@ -4358,25 +4302,7 @@ static R F3() comp.VerifyEmitDiagnostics(); comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (11,9): error CS8350: This combination of arguments to 'R.F(in int)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope - // r1.F(in i); // 1 - Diagnostic(ErrorCode.ERR_CallArgMixing, "r1.F(in i)").WithArguments("R.F(in int)", "t").WithLocation(11, 9), - // (11,17): error CS8168: Cannot return local 'i' by reference because it is not a ref local - // r1.F(in i); // 1 - Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(11, 17), - // (18,9): error CS8350: This combination of arguments to 'R.F(in int)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope - // r2.F(i); // 2 - Diagnostic(ErrorCode.ERR_CallArgMixing, "r2.F(i)").WithArguments("R.F(in int)", "t").WithLocation(18, 9), - // (18,14): error CS8168: Cannot return local 'i' by reference because it is not a ref local - // r2.F(i); // 2 - Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(18, 14), - // (24,9): error CS8350: This combination of arguments to 'R.F(in int)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope - // r3.F(3); // 3 - Diagnostic(ErrorCode.ERR_CallArgMixing, "r3.F(3)").WithArguments("R.F(in int)", "t").WithLocation(24, 9), - // (24,14): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference - // r3.F(3); // 3 - Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "3").WithLocation(24, 14)); + comp.VerifyEmitDiagnostics(); } [Theory] @@ -4588,7 +4514,7 @@ static void M2(T t2) } static void M3(ref T t3) { - S s3; + scoped S s3; s3 = new S(ref t3); s3 = new S { F = t3 }; } @@ -4663,7 +4589,7 @@ static void M2(T t2) } static void M3(ref T t3) { - S s3; + scoped S s3; s3 = new S(ref t3); s3 = new S { F = t3 }; } @@ -6005,66 +5931,73 @@ public void AssignRefTo_RefField() class Program { static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 - static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } - static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 2 - static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 3 + static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } // 2 + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 3 + static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 4 - static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 4 - static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } - static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 5 - static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 6 + static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 5 + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } // 6 + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 7 + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 8 - static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 7 + static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 9 static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } - static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 8 - static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 9 + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 10 + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 11 - static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 10 - static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 11 - static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 12 - static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 13 + static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 12 + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 13 + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 14 + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 15 }"; var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); comp.VerifyEmitDiagnostics( // (9,59): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s.F = ref tValue").WithArguments("F", "tValue").WithLocation(9, 59), + // (10,59): error CS9079: Cannot ref-assign 'tRef' to 'F' because 'tRef' can only escape the current method through a return statement. + // static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } // 2 + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "s.F = ref tRef").WithArguments("F", "tRef").WithLocation(10, 59), // (11,75): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. - // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 2 + // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 3 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s.F = ref tOut").WithArguments("F", "tOut").WithLocation(11, 75), // (12,69): error CS8331: Cannot assign to variable 'in T' or use it as the right hand side of a ref assignment because it is a readonly variable - // static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 3 + // static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 4 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(12, 69), // (14,64): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. - // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 4 + // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 5 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sRef.F = ref tValue").WithArguments("F", "tValue").WithLocation(14, 64), + // (15,64): error CS9079: Cannot ref-assign 'tRef' to 'F' because 'tRef' can only escape the current method through a return statement. + // static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } // 6 + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "sRef.F = ref tRef").WithArguments("F", "tRef").WithLocation(15, 64), // (16,80): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. - // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 5 + // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 7 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sRef.F = ref tOut").WithArguments("F", "tOut").WithLocation(16, 80), // (17,77): error CS8331: Cannot assign to variable 'in T' or use it as the right hand side of a ref assignment because it is a readonly variable - // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 6 + // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 8 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(17, 77), // (19,80): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. - // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 7 + // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 9 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sOut.F = ref tValue").WithArguments("F", "tValue").WithLocation(19, 80), // (21,96): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. - // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 8 + // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 10 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sOut.F = ref tOut").WithArguments("F", "tOut").WithLocation(21, 96), // (22,93): error CS8331: Cannot assign to variable 'in T' or use it as the right hand side of a ref assignment because it is a readonly variable - // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 9 + // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 11 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(22, 93), // (24,61): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable - // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 10 + // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 12 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(24, 61), // (25,61): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable - // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 11 + // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 13 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(25, 61), // (26,77): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable - // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 12 + // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 14 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(26, 77), // (27,61): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable - // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 13 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61)); + // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 15 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61) + ); // Valid cases from above. source = @@ -6078,10 +6011,6 @@ ref struct S class Program { - static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } - - static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } - static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } static void Main() @@ -6089,16 +6018,6 @@ static void Main() int x, y; scoped S s; - x = 1; y = 2; - s = new S(ref x); - AssignRefToValue(s, ref y); - Console.WriteLine(s.F); - - x = 3; y = 4; - s = new S(ref x); - AssignRefToRef(ref s, ref y); - Console.WriteLine(s.F); - x = 5; y = 6; s = new S(ref x); AssignRefToOut(out s, ref y); @@ -6107,28 +6026,9 @@ static void Main() }"; comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( -@"1 -4 -6")); +@"6")); + verifier.VerifyILMultiple( - "Program.AssignRefToValue", -@"{ - // Code size 9 (0x9) - .maxstack 2 - IL_0000: ldarga.s V_0 - IL_0002: ldarg.1 - IL_0003: stfld ""ref T S.F"" - IL_0008: ret -}", - "Program.AssignRefToRef", -@"{ - // Code size 8 (0x8) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldarg.1 - IL_0002: stfld ""ref T S.F"" - IL_0007: ret -}", "Program.AssignRefToOut", @"{ // Code size 15 (0xf) @@ -6179,15 +6079,27 @@ class Program // (9,59): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s.F = ref tValue").WithArguments("F", "tValue").WithLocation(9, 59), + // (10,59): error CS9079: Cannot ref-assign 'tRef' to 'F' because 'tRef' can only escape the current method through a return statement. + // static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "s.F = ref tRef").WithArguments("F", "tRef").WithLocation(10, 59), // (11,75): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 2 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s.F = ref tOut").WithArguments("F", "tOut").WithLocation(11, 75), + // (12,59): error CS9079: Cannot ref-assign 'tIn' to 'F' because 'tIn' can only escape the current method through a return statement. + // static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "s.F = ref tIn").WithArguments("F", "tIn").WithLocation(12, 59), // (14,64): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 3 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sRef.F = ref tValue").WithArguments("F", "tValue").WithLocation(14, 64), + // (15,64): error CS9079: Cannot ref-assign 'tRef' to 'F' because 'tRef' can only escape the current method through a return statement. + // static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "sRef.F = ref tRef").WithArguments("F", "tRef").WithLocation(15, 64), // (16,80): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 4 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sRef.F = ref tOut").WithArguments("F", "tOut").WithLocation(16, 80), + // (17,64): error CS9079: Cannot ref-assign 'tIn' to 'F' because 'tIn' can only escape the current method through a return statement. + // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "sRef.F = ref tIn").WithArguments("F", "tIn").WithLocation(17, 64), // (19,80): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 5 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sOut.F = ref tValue").WithArguments("F", "tValue").WithLocation(19, 80), @@ -6219,12 +6131,6 @@ ref struct S class Program { - static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } - static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } - - static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } - static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } - static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } @@ -6233,24 +6139,6 @@ static void Main() int x, y; scoped S s; - x = 1; y = 2; - s = new S(ref x); - AssignRefToValue(s, ref y); - Console.WriteLine(s.F); - x = 1; y = 2; - s = new S(ref x); - AssignInToValue(s, y); - Console.WriteLine(s.F); - - x = 3; y = 4; - s = new S(ref x); - AssignRefToRef(ref s, ref y); - Console.WriteLine(s.F); - x = 3; y = 4; - s = new S(ref x); - AssignInToRef(ref s, y); - Console.WriteLine(s.F); - x = 5; y = 6; s = new S(ref x); AssignRefToOut(out s, ref y); @@ -6263,49 +6151,9 @@ static void Main() }"; comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( -@"1 -1 -4 -4 -6 +@"6 6")); verifier.VerifyILMultiple( - "Program.AssignRefToValue", -@"{ - // Code size 9 (0x9) - .maxstack 2 - IL_0000: ldarga.s V_0 - IL_0002: ldarg.1 - IL_0003: stfld ""ref readonly T S.F"" - IL_0008: ret -}", - "Program.AssignInToValue", -@"{ - // Code size 9 (0x9) - .maxstack 2 - IL_0000: ldarga.s V_0 - IL_0002: ldarg.1 - IL_0003: stfld ""ref readonly T S.F"" - IL_0008: ret -}", - "Program.AssignRefToRef", -@"{ - // Code size 8 (0x8) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldarg.1 - IL_0002: stfld ""ref readonly T S.F"" - IL_0007: ret -}", - "Program.AssignInToRef", -@"{ - // Code size 8 (0x8) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldarg.1 - IL_0002: stfld ""ref readonly T S.F"" - IL_0007: ret -}", "Program.AssignRefToOut", @"{ // Code size 15 (0xf) @@ -7424,10 +7272,7 @@ static T ReadIn(in R2 r2In) comp.VerifyEmitDiagnostics( // (10,12): error CS9050: A ref field cannot refer to a ref struct. // public ref R1 R1; - Diagnostic(ErrorCode.ERR_RefFieldCannotReferToRefStruct, "ref R1").WithLocation(10, 12), - // (11,31): error CS9079: Cannot ref-assign 'r1' to 'R1' because 'r1' can only escape the current method through a return statement. - // public R2(ref R1 r1) { R1 = ref r1; } - Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "R1 = ref r1").WithArguments("R1", "r1").WithLocation(11, 31)); + Diagnostic(ErrorCode.ERR_RefFieldCannotReferToRefStruct, "ref R1").WithLocation(10, 12)); } [Fact] @@ -8614,10 +8459,9 @@ static ref T ReturnOutParamByRef(out T t) Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "t").WithArguments("t").WithLocation(6, 20)); } - // Breaking change in C#11: Instance method on ref struct instance may capture unscoped ref or in arguments. [Theory] [CombinatorialData] - public void BreakingChange_RefStructInstanceMethodMayCaptureRef( + public void InstanceMethodCannotCaptureRefByRef( [CombinatorialValues(LanguageVersion.CSharp10, LanguageVersion.CSharp11)] LanguageVersion languageVersionA, [CombinatorialValues(LanguageVersion.CSharp10, LanguageVersion.CSharp11)] LanguageVersion languageVersionB, bool useCompilationReference) @@ -8642,20 +8486,7 @@ static R Use(R r) } }"; comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersionB)); - if (languageVersionA == LanguageVersion.CSharp10) - { - comp.VerifyEmitDiagnostics(); - } - else - { - comp.VerifyEmitDiagnostics( - // (6,9): error CS8350: This combination of arguments to 'R.MayCaptureArg(ref int)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope - // r.MayCaptureArg(ref i); - Diagnostic(ErrorCode.ERR_CallArgMixing, "r.MayCaptureArg(ref i)").WithArguments("R.MayCaptureArg(ref int)", "t").WithLocation(6, 9), - // (6,29): error CS8168: Cannot return local 'i' by reference because it is not a ref local - // r.MayCaptureArg(ref i); - Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(6, 29)); - } + comp.VerifyEmitDiagnostics(); } [Theory] @@ -8876,20 +8707,7 @@ static void Main() } }"; comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersionB)); - if (languageVersionA == LanguageVersion.CSharp10) - { - comp.VerifyEmitDiagnostics(); - } - else - { - comp.VerifyEmitDiagnostics( - // (6,9): error CS8350: This combination of arguments to 'A.MayCaptureRef(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope - // MayCaptureRef(__arglist(ref r)); // error: may expose variables outside of their declaration scope - Diagnostic(ErrorCode.ERR_CallArgMixing, "MayCaptureRef(__arglist(ref r))").WithArguments("A.MayCaptureRef(__arglist)", "__arglist").WithLocation(6, 9), - // (6,37): error CS8168: Cannot return local 'r' by reference because it is not a ref local - // MayCaptureRef(__arglist(ref r)); // error: may expose variables outside of their declaration scope - Diagnostic(ErrorCode.ERR_RefReturnLocal, "r").WithArguments("r").WithLocation(6, 37)); - } + comp.VerifyEmitDiagnostics(); } [Theory] @@ -9495,7 +9313,7 @@ static void Main() R a = new R(); R b = new R(); R c = new R(); - R d; + scoped R d; A.F(a, ref b, in c, out d); } }"; @@ -15453,10 +15271,23 @@ static void F(out R r) } }"; var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); - comp.VerifyDiagnostics( - // (13,9): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope - // r1.F(out r); - Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("r1").WithLocation(13, 9)); + if (languageVersion == LanguageVersion.CSharp10) + { + comp.VerifyDiagnostics( + // (13,9): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope + // r1.F(out r); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("r1").WithLocation(13, 9)); + } + else + { + comp.VerifyDiagnostics( + // (13,9): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope + // r1.F(out r); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("r1").WithLocation(13, 9), + // (13,9): error CS8350: This combination of arguments to 'R.F(out R)' is disallowed because it may expose variables referenced by parameter 'this' outside of their declaration scope + // r1.F(out r); + Diagnostic(ErrorCode.ERR_CallArgMixing, "r1.F(out r)").WithArguments("R.F(out R)", "this").WithLocation(13, 9)); + } } [Fact] @@ -15482,7 +15313,10 @@ static void F(out R r) comp.VerifyDiagnostics( // (13,9): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope // r1.F(out r); - Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("r1").WithLocation(13, 9)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("r1").WithLocation(13, 9), + // (13,9): error CS8350: This combination of arguments to 'R.F(out R)' is disallowed because it may expose variables referenced by parameter 'this' outside of their declaration scope + // r1.F(out r); + Diagnostic(ErrorCode.ERR_CallArgMixing, "r1.F(out r)").WithArguments("R.F(out R)", "this").WithLocation(13, 9)); } [Theory] @@ -15506,13 +15340,20 @@ static void Main() } }"; var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); - comp.VerifyDiagnostics( - // (12,9): error CS8350: This combination of arguments to 'R.F(out Span)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope - // r.F(out s); - Diagnostic(ErrorCode.ERR_CallArgMixing, "r.F(out s)").WithArguments("R.F(out System.Span)", "s").WithLocation(12, 9), - // (12,17): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope - // r.F(out s); - Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(12, 17)); + if (languageVersion == LanguageVersion.CSharp10) + { + comp.VerifyDiagnostics( + // (12,9): error CS8350: This combination of arguments to 'R.F(out Span)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope + // r.F(out s); + Diagnostic(ErrorCode.ERR_CallArgMixing, "r.F(out s)").WithArguments("R.F(out System.Span)", "s").WithLocation(12, 9), + // (12,17): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // r.F(out s); + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(12, 17)); + } + else + { + comp.VerifyDiagnostics(); + } } [WorkItem(63016, "https://github.com/dotnet/roslyn/issues/63016")] @@ -15535,12 +15376,9 @@ private void M2(ref int i) }"; var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); comp.VerifyDiagnostics( - // (7,9): error CS8350: This combination of arguments to 'R.M2(ref int)' is disallowed because it may expose variables referenced by parameter 'i' outside of their declaration scope - // M2(ref i); - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref i)").WithArguments("R.M2(ref int)", "i").WithLocation(7, 9), - // (7,16): error CS8168: Cannot return local 'i' by reference because it is not a ref local - // M2(ref i); - Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(7, 16)); + // (11,9): error CS9079: Cannot ref-assign 'i' to '_i' because 'i' can only escape the current method through a return statement. + // _i = ref i; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "_i = ref i").WithArguments("_i", "i").WithLocation(11, 9)); } [WorkItem(63016, "https://github.com/dotnet/roslyn/issues/63016")] @@ -15574,12 +15412,9 @@ private void M2(ref int i) }"; var comp = CreateCompilation(new[] { sourceA, sourceB }, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); comp.VerifyDiagnostics( - // (8,9): error CS8350: This combination of arguments to 'S.M2(ref int)' is disallowed because it may expose variables referenced by parameter 'i' outside of their declaration scope - // M2(ref s[0]); - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s[0])").WithArguments("S.M2(ref int)", "i").WithLocation(8, 9), - // (8,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope - // M2(ref s[0]); - Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(8, 16)); + // (12,9): error CS9079: Cannot ref-assign 'i' to '_i' because 'i' can only escape the current method through a return statement. + // _i = ref i; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "_i = ref i").WithArguments("_i", "i").WithLocation(12, 9)); } [Fact] @@ -18006,14 +17841,17 @@ R this[int i] { get { return default; } [UnscopedRef] - set { value.F = ref this; } + set { value.F = ref this; } // 2 } }"; var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); comp.VerifyDiagnostics( // (11,15): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. // set { value.F = ref this; } // 1 - Diagnostic(ErrorCode.ERR_RefAssignNarrower, "value.F = ref this").WithArguments("F", "this").WithLocation(11, 15)); + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "value.F = ref this").WithArguments("F", "this").WithLocation(11, 15), + // (20,15): error CS9079: Cannot ref-assign 'this' to 'F' because 'this' can only escape the current method through a return statement. + // set { value.F = ref this; } // 2 + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "value.F = ref this").WithArguments("F", "this").WithLocation(20, 15)); } [Fact] @@ -18362,7 +18200,8 @@ ref int F2B() Diagnostic(ErrorCode.ERR_EscapeCall, "F2A(ref r)").WithArguments("A.F2A(ref R)", "r2").WithLocation(13, 20), // (13,28): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope // return ref F2A(ref r); // 2 - Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(13, 28)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(13, 28) + ); var baseType = comp.GetMember("B1").BaseTypeNoUseSiteDiagnostics; VerifyParameterSymbol(baseType.GetMethod("F1A").Parameters[0], "ref R r1", RefKind.Ref, DeclarationScope.Unscoped); @@ -21350,7 +21189,7 @@ ref struct RSOut class Program { RS M1(ref RS rs) => rs; - void M2(ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 1 + void M2(ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); RS M3(ref RS rs) { @@ -21358,14 +21197,14 @@ RS M3(ref RS rs) } void M4(ref RS rs, out RSOut rs1) { - rs1 = rs.ToRSOut(); // 2 + rs1 = rs.ToRSOut(); } void localContainer() { #pragma warning disable 8321 RS M1(ref RS rs) => rs; - void M2(ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 3 + void M2(ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); RS M3(ref RS rs) { @@ -21383,7 +21222,7 @@ void M4(ref RS rs, out RSOut rs1) void lambdaContainer() { ReturnsRefStruct d1 = (ref RS rs) => rs; - RefStructOut d2 = (ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 5 + RefStructOut d2 = (ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); ReturnsRefStruct d3 = (ref RS rs) => { @@ -21391,32 +21230,14 @@ void lambdaContainer() }; RefStructOut d4 = (ref RS rs, out RSOut rs1) => { - rs1 = rs.ToRSOut(); // 6 + rs1 = rs.ToRSOut(); }; } } """; var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); - comp.VerifyDiagnostics( - // (22,48): error CS9077: Cannot return a parameter by reference 'rs' through a ref parameter; it can only be returned in a return statement - // void M2(ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 1 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "rs").WithArguments("rs").WithLocation(22, 48), - // (30,15): error CS9077: Cannot return a parameter by reference 'rs' through a ref parameter; it can only be returned in a return statement - // rs1 = rs.ToRSOut(); // 2 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "rs").WithArguments("rs").WithLocation(30, 15), - // (37,52): error CS9077: Cannot return a parameter by reference 'rs' through a ref parameter; it can only be returned in a return statement - // void M2(ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 3 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "rs").WithArguments("rs").WithLocation(37, 52), - // (45,19): error CS9077: Cannot return a parameter by reference 'rs' through a ref parameter; it can only be returned in a return statement - // rs1 = rs.ToRSOut(); // 4 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "rs").WithArguments("rs").WithLocation(45, 19), - // (55,63): error CS9077: Cannot return a parameter by reference 'rs' through a ref parameter; it can only be returned in a return statement - // RefStructOut d2 = (ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 5 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "rs").WithArguments("rs").WithLocation(55, 63), - // (63,19): error CS9077: Cannot return a parameter by reference 'rs' through a ref parameter; it can only be returned in a return statement - // rs1 = rs.ToRSOut(); // 6 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "rs").WithArguments("rs").WithLocation(63, 19)); + comp.VerifyDiagnostics(); } [Fact, WorkItem(63526, "https://github.com/dotnet/roslyn/issues/63526")] @@ -21442,33 +21263,33 @@ static void M2(scoped ref S p1, out S p2) { static void M3(out S p1, ref S p2) { p1 = default; p2.refField = ref p1.field; // 3 - p2.refField = ref p1.refField; // Okay + p2.refField = ref p1.refField; // 4 } // The [UnscopedRef] moves `out` to default RSTE which is *return only* static void M4([UnscopedRef] out S p1, ref S p2) { p1 = default; - p2.refField = ref p1.field; // 4 - p2.refField = ref p1.refField; // Okay + p2.refField = ref p1.field; // 5 + p2.refField = ref p1.refField; // 6 } static void M5(ref S p1, ref S2 p2) { - p2 = Inner1(ref p1); // 5 + p2 = Inner1(ref p1); // 7 p2 = Inner2(ref p1); // Okay } static void M6(ref S p1, out S2 p2) { - p2 = Inner1(ref p1); // 6 + p2 = Inner1(ref p1); // Okay p2 = Inner2(ref p1); // Okay } static void M7(scoped ref S p1, ref S2 p2) { - p2 = Inner1(ref p1); // 7 + p2 = Inner1(ref p1); // 8 p2 = Inner2(ref p1); // Okay } static S2 M8(scoped ref S p) { - if (condition()) return Inner1(ref p); // 8 + if (condition()) return Inner1(ref p); // 9 if (condition()) return Inner2(ref p); // Okay throw null!; @@ -21505,33 +21326,34 @@ ref struct S2 { // (19,5): error CS8374: Cannot ref-assign 'p1.field' to 'refField' because 'p1.field' has a narrower escape scope than 'refField'. // p2.refField = ref p1.field; // 3 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "p2.refField = ref p1.field").WithArguments("refField", "p1.field").WithLocation(19, 5), + // (20,5): error CS9079: Cannot ref-assign 'p1.refField' to 'refField' because 'p1.refField' can only escape the current method through a return statement. + // p2.refField = ref p1.refField; // 4 + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "p2.refField = ref p1.refField").WithArguments("refField", "p1.refField").WithLocation(20, 5), // (26,5): error CS9079: Cannot ref-assign 'p1.field' to 'refField' because 'p1.field' can only escape the current method through a return statement. - // p2.refField = ref p1.field; // 4 + // p2.refField = ref p1.field; // 5 Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "p2.refField = ref p1.field").WithArguments("refField", "p1.field").WithLocation(26, 5), + // (27,5): error CS9079: Cannot ref-assign 'p1.refField' to 'refField' because 'p1.refField' can only escape the current method through a return statement. + // p2.refField = ref p1.refField; // 6 + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "p2.refField = ref p1.refField").WithArguments("refField", "p1.refField").WithLocation(27, 5), // (31,10): error CS8347: Cannot use a result of 'Inner1(ref S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope - // p2 = Inner1(ref p1); // 5 + // p2 = Inner1(ref p1); // 7 Diagnostic(ErrorCode.ERR_EscapeCall, "Inner1(ref p1)").WithArguments("Inner1(ref S)", "s").WithLocation(31, 10), // (31,21): error CS9077: Cannot return a parameter by reference 'p1' through a ref parameter; it can only be returned in a return statement - // p2 = Inner1(ref p1); // 5 + // p2 = Inner1(ref p1); // 7 Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "p1").WithArguments("p1").WithLocation(31, 21), - // (36,10): error CS8347: Cannot use a result of 'Inner1(ref S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope - // p2 = Inner1(ref p1); // 6 - Diagnostic(ErrorCode.ERR_EscapeCall, "Inner1(ref p1)").WithArguments("Inner1(ref S)", "s").WithLocation(36, 10), - // (36,21): error CS9077: Cannot return a parameter by reference 'p1' through a ref parameter; it can only be returned in a return statement - // p2 = Inner1(ref p1); // 6 - Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "p1").WithArguments("p1").WithLocation(36, 21), // (41,10): error CS8347: Cannot use a result of 'Inner1(ref S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope - // p2 = Inner1(ref p1); // 7 + // p2 = Inner1(ref p1); // 8 Diagnostic(ErrorCode.ERR_EscapeCall, "Inner1(ref p1)").WithArguments("Inner1(ref S)", "s").WithLocation(41, 10), // (41,21): error CS9075: Cannot return a parameter by reference 'p1' because it is scoped to the current method - // p2 = Inner1(ref p1); // 7 + // p2 = Inner1(ref p1); // 8 Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "p1").WithArguments("p1").WithLocation(41, 21), // (46,29): error CS8347: Cannot use a result of 'Inner1(ref S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope - // if (condition()) return Inner1(ref p); // 8 + // if (condition()) return Inner1(ref p); // 9 Diagnostic(ErrorCode.ERR_EscapeCall, "Inner1(ref p)").WithArguments("Inner1(ref S)", "s").WithLocation(46, 29), // (46,40): error CS9075: Cannot return a parameter by reference 'p' because it is scoped to the current method - // if (condition()) return Inner1(ref p); // 8 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "p").WithArguments("p").WithLocation(46, 40)); + // if (condition()) return Inner1(ref p); // 9 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "p").WithArguments("p").WithLocation(46, 40) + ); } [Fact, WorkItem(63526, "https://github.com/dotnet/roslyn/issues/63526")] @@ -21557,33 +21379,33 @@ static unsafe void M2(scoped ref S p1, out S p2) { static unsafe void M3(out S p1, ref S p2) { p1 = default; p2.refField = ref p1.field; // 3 - p2.refField = ref p1.refField; // Okay + p2.refField = ref p1.refField; // 4 } // The [UnscopedRef] moves `out` to default RSTE which is *return only* static unsafe void M4([UnscopedRef] out S p1, ref S p2) { p1 = default; - p2.refField = ref p1.field; // 4 - p2.refField = ref p1.refField; // Okay + p2.refField = ref p1.field; // 5 + p2.refField = ref p1.refField; // 6 } static unsafe void M5(ref S p1, ref S2 p2) { - p2 = Inner1(ref p1); // 5 + p2 = Inner1(ref p1); // 7 p2 = Inner2(ref p1); // Okay } static unsafe void M6(ref S p1, out S2 p2) { - p2 = Inner1(ref p1); // 6 + p2 = Inner1(ref p1); // Okay p2 = Inner2(ref p1); // Okay } static unsafe void M7(scoped ref S p1, ref S2 p2) { - p2 = Inner1(ref p1); // 7 + p2 = Inner1(ref p1); // 8 p2 = Inner2(ref p1); // Okay } static unsafe S2 M8(scoped ref S p) { - if (condition()) return Inner1(ref p); // 8 + if (condition()) return Inner1(ref p); // 9 if (condition()) return Inner2(ref p); // Okay throw null!; @@ -21620,21 +21442,25 @@ ref struct S2 { // (19,5): warning CS9085: This ref-assigns 'p1.field' to 'refField' but 'p1.field' has a narrower escape scope than 'refField'. // p2.refField = ref p1.field; // 3 Diagnostic(ErrorCode.WRN_RefAssignNarrower, "p2.refField = ref p1.field").WithArguments("refField", "p1.field").WithLocation(19, 5), + // (20,5): warning CS9093: This ref-assigns 'p1.refField' to 'refField' but 'p1.refField' can only escape the current method through a return statement. + // p2.refField = ref p1.refField; // 4 + Diagnostic(ErrorCode.WRN_RefAssignReturnOnly, "p2.refField = ref p1.refField").WithArguments("refField", "p1.refField").WithLocation(20, 5), // (26,5): warning CS9093: This ref-assigns 'p1.field' to 'refField' but 'p1.field' can only escape the current method through a return statement. - // p2.refField = ref p1.field; // 4 + // p2.refField = ref p1.field; // 5 Diagnostic(ErrorCode.WRN_RefAssignReturnOnly, "p2.refField = ref p1.field").WithArguments("refField", "p1.field").WithLocation(26, 5), + // (27,5): warning CS9093: This ref-assigns 'p1.refField' to 'refField' but 'p1.refField' can only escape the current method through a return statement. + // p2.refField = ref p1.refField; // 6 + Diagnostic(ErrorCode.WRN_RefAssignReturnOnly, "p2.refField = ref p1.refField").WithArguments("refField", "p1.refField").WithLocation(27, 5), // (31,21): warning CS9094: This returns a parameter by reference 'p1' through a ref parameter; but it can only safely be returned in a return statement - // p2 = Inner1(ref p1); // 5 + // p2 = Inner1(ref p1); // 7 Diagnostic(ErrorCode.WRN_RefReturnOnlyParameter, "p1").WithArguments("p1").WithLocation(31, 21), - // (36,21): warning CS9094: This returns a parameter by reference 'p1' through a ref parameter; but it can only safely be returned in a return statement - // p2 = Inner1(ref p1); // 6 - Diagnostic(ErrorCode.WRN_RefReturnOnlyParameter, "p1").WithArguments("p1").WithLocation(36, 21), // (41,21): warning CS9088: This returns a parameter by reference 'p1' but it is scoped to the current method - // p2 = Inner1(ref p1); // 7 + // p2 = Inner1(ref p1); // 8 Diagnostic(ErrorCode.WRN_RefReturnScopedParameter, "p1").WithArguments("p1").WithLocation(41, 21), // (46,40): warning CS9088: This returns a parameter by reference 'p' but it is scoped to the current method - // if (condition()) return Inner1(ref p); // 8 - Diagnostic(ErrorCode.WRN_RefReturnScopedParameter, "p").WithArguments("p").WithLocation(46, 40)); + // if (condition()) return Inner1(ref p); // 9 + Diagnostic(ErrorCode.WRN_RefReturnScopedParameter, "p").WithArguments("p").WithLocation(46, 40) + ); } [Fact, WorkItem(63526, "https://github.com/dotnet/roslyn/issues/63526")] @@ -21666,7 +21492,7 @@ void M2(ref S p) { [UnscopedRef] void M3(out S p) { p = default; - p.refField = ref this.field; // 3 + p.refField = ref this.field; // Okay (was 3) p.refField = ref this.refField; // Okay } @@ -21693,9 +21519,6 @@ void M5(out S2 p) { // (19,9): error CS9079: Cannot ref-assign 'this.field' to 'refField' because 'this.field' can only escape the current method through a return statement. // p.refField = ref this.field; // 2 Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "p.refField = ref this.field").WithArguments("refField", "this.field").WithLocation(19, 9), - // (26,9): error CS9079: Cannot ref-assign 'this.field' to 'refField' because 'this.field' can only escape the current method through a return statement. - // p.refField = ref this.field; // 3 - Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "p.refField = ref this.field").WithArguments("refField", "this.field").WithLocation(26, 9), // (31,13): error CS8347: Cannot use a result of 'S.Inner1(ref S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope // p = Inner1(ref this); // 4 Diagnostic(ErrorCode.ERR_EscapeCall, "Inner1(ref this)").WithArguments("S.Inner1(ref S)", "s").WithLocation(31, 13), @@ -21739,7 +21562,7 @@ void M2(ref S p) { [UnscopedRef] void M3(out S p) { p = default; - p.refField = ref this.field; // 3 + p.refField = ref this.field; // Okay (was 3) p.refField = ref this.refField; // Okay } @@ -21759,22 +21582,18 @@ void M5(out S2 p) { """; var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( - // (13,9): warning CS9085: The right-hand-side expression 'this.field' has a narrower escape scope than the left-hand-side expression 'refField' in ref-assignment. + // (13,9): warning CS9085: This ref-assigns 'this.field' to 'refField' but 'this.field' has a narrower escape scope than 'refField'. // p.refField = ref this.field; // 1 Diagnostic(ErrorCode.WRN_RefAssignNarrower, "p.refField = ref this.field").WithArguments("refField", "this.field").WithLocation(13, 9), // (19,9): warning CS9093: This ref-assigns 'this.field' to 'refField' but 'this.field' can only escape the current method through a return statement. // p.refField = ref this.field; // 2 Diagnostic(ErrorCode.WRN_RefAssignReturnOnly, "p.refField = ref this.field").WithArguments("refField", "this.field").WithLocation(19, 9), - // (26,9): warning CS9093: This ref-assigns 'this.field' to 'refField' but 'this.field' can only escape the current method through a return statement. - // p.refField = ref this.field; // 3 - Diagnostic(ErrorCode.WRN_RefAssignReturnOnly, "p.refField = ref this.field").WithArguments("refField", "this.field").WithLocation(26, 9), // (31,24): warning CS9084: Struct member returns 'this' or other instance members by reference // p = Inner1(ref this); // 4 Diagnostic(ErrorCode.WRN_RefReturnStructThis, "this").WithLocation(31, 24), // (36,24): warning CS9084: Struct member returns 'this' or other instance members by reference // p = Inner1(ref this); // 5 - Diagnostic(ErrorCode.WRN_RefReturnStructThis, "this").WithLocation(36, 24) - ); + Diagnostic(ErrorCode.WRN_RefReturnStructThis, "this").WithLocation(36, 24)); } [Fact] @@ -21806,10 +21625,205 @@ public RefByteContainer Create(ref ByteContainer bc) """; var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics(); + } + + /// + /// Validate that this is properly represented as an out parameter in a constructor and + /// can capture ref as ref. + /// + [Fact] + public void OutReturnOnly_Ctor1() + { + var source = """ + ref struct RS + { + ref int field; + public RS(ref int i) + { + field = ref i; + } + + static RS M1(ref int i) => new RS(ref i); + static RS M2() + { + int i = 0; + return new RS(ref i); + } + } + """; + var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics( + // (13,16): error CS8347: Cannot use a result of 'RS.RS(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new RS(ref i); + Diagnostic(ErrorCode.ERR_EscapeCall, "new RS(ref i)").WithArguments("RS.RS(ref int)", "i").WithLocation(13, 16), + // (13,27): error CS8168: Cannot return local 'i' by reference because it is not a ref local + // return new RS(ref i); + Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(13, 27)); + } + + /// + /// Out and ref have different return scopes + /// + [Fact] + public void OutReturnOnly_1() + { + var source = """ + ref struct RS + { + ref int field; + public RS(ref int i) + { + field = ref i; + } + + static void M1(ref int i, ref RS rs) => rs = new RS(ref i); // 1 + static void M2(ref int i, out RS rs) => rs = new RS(ref i); + } + """; + var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics( + // (9,50): error CS8347: Cannot use a result of 'RS.RS(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // static void M1(ref int i, ref RS rs) => rs = new RS(ref i); // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "new RS(ref i)").WithArguments("RS.RS(ref int)", "i").WithLocation(9, 50), + // (9,61): error CS9077: Cannot return a parameter by reference 'i' through a ref parameter; it can only be returned in a return statement + // static void M1(ref int i, ref RS rs) => rs = new RS(ref i); // 1 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "i").WithArguments("i").WithLocation(9, 61)); + } + + /// + /// Out and in have different return scopes + /// + [Fact] + public void OutReturnOnly_2() + { + var source = """ + readonly ref struct RS + { + readonly ref readonly int field; + public RS(in int i) + { + field = ref i; + } + + static void M1(in int i, ref RS rs) => rs = new RS(in i); // 1 + static void M2(in int i, out RS rs) => rs = new RS(in i); + } + """; + var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics( + // (9,49): error CS8347: Cannot use a result of 'RS.RS(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // static void M1(in int i, ref RS rs) => rs = new RS(in i); // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "new RS(in i)").WithArguments("RS.RS(in int)", "i").WithLocation(9, 49), + // (9,59): error CS9077: Cannot return a parameter by reference 'i' through a ref parameter; it can only be returned in a return statement + // static void M1(in int i, ref RS rs) => rs = new RS(in i); // 1 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "i").WithArguments("i").WithLocation(9, 59)); + } + + /// + /// Out and ref have different return scopes + /// + [Fact] + public void OutReturnOnly_3() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + ref struct RS + { + int field; + ref int refField; + public RS(ref int i) + { + refField = ref i; + } + + static void M1(ref RS i, ref RS rs) => rs = new RS(ref i.field); // 1 + static void M2(ref RS i, out RS rs) => rs = new RS(ref i.field); + static void M3(ref RS i, [UnscopedRef] out RS rs) => rs = new RS(ref i.field); + } + """; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); comp.VerifyDiagnostics( - // (12,9): error CS9079: Cannot ref-assign 'bc.B' to 'RB' because 'bc.B' can only escape the current method through a return statement. - // RB = ref bc.B; // 1 - Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "RB = ref bc.B").WithArguments("RB", "bc.B").WithLocation(12, 9)); + // (11,49): error CS8347: Cannot use a result of 'RS.RS(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // static void M1(ref RS i, ref RS rs) => rs = new RS(ref i.field); // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "new RS(ref i.field)").WithArguments("RS.RS(ref int)", "i").WithLocation(11, 49), + // (11,60): error CS9078: Cannot return by reference a member of parameter 'i' through a ref parameter; it can only be returned in a return statement + // static void M1(ref RS i, ref RS rs) => rs = new RS(ref i.field); // 1 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter2, "i").WithArguments("i").WithLocation(11, 60)); + } + + [Fact] + [WorkItem(62094, "https://github.com/dotnet/roslyn/issues/62094")] + public void OutReturnOnly_4() + { + var source = """ + ref struct R + { + public R(ref int i) { } + } + + class Program + { + static void F0(ref R a, out R b) + { + b = a; + } + + static void F1(ref R x) + { + int i = 1; + R y = new R(ref i); + F0(ref x, out y); + } + } + """; + var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics(); + } + + [Fact] + public void OutReturnOnly_5() + { + var source = """ + ref struct R + { + public R(ref int i) { } + } + + class Program + { + public static void F1(R a, out R d) => throw null; + public static void F2(ref R a, out R d) => throw null; + public static void F3(in R a, out R d) => throw null; + public static void F4(scoped ref R a, out R d) => throw null; + public static void F5(scoped in R a, out R d) => throw null; + + public void Test() + { + R local = default; + F1(local, out local); + F2(ref local, out local); // 1 + F3(in local, out local); // 2 + F4(ref local, out local); + F5(in local, out local); + } + } + """; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics( + // (18,9): error CS8350: This combination of arguments to 'Program.F2(ref R, out R)' is disallowed because it may expose variables referenced by parameter 'a' outside of their declaration scope + // F2(ref local, out local); // 1 + Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref local, out local)").WithArguments("Program.F2(ref R, out R)", "a").WithLocation(18, 9), + // (18,16): error CS8168: Cannot return local 'local' by reference because it is not a ref local + // F2(ref local, out local); // 1 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "local").WithArguments("local").WithLocation(18, 16), + // (19,9): error CS8350: This combination of arguments to 'Program.F3(in R, out R)' is disallowed because it may expose variables referenced by parameter 'a' outside of their declaration scope + // F3(in local, out local); // 2 + Diagnostic(ErrorCode.ERR_CallArgMixing, "F3(in local, out local)").WithArguments("Program.F3(in R, out R)", "a").WithLocation(19, 9), + // (19,15): error CS8168: Cannot return local 'local' by reference because it is not a ref local + // F3(in local, out local); // 2 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "local").WithArguments("local").WithLocation(19, 15) + ); } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index a04f265b41833..a60fc4d8dc062 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -4538,9 +4538,11 @@ public void M(int value) Assert.Equal(SpecialType.System_Int32, model.GetTypeInfo(right).Type.SpecialType); } - [Fact] + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] [WorkItem(27772, "https://github.com/dotnet/roslyn/issues/27772")] - public void RefReturnInvocationOfRefLikeTypeRefResult() + public void RefReturnInvocationOfRefLikeTypeRefResult(LanguageVersion langVersion) { string source = @" class C @@ -4566,7 +4568,7 @@ ref struct S public ref long M(ref long x) => ref x; }"; - var comp = CreateCompilationWithMscorlibAndSpan(source, parseOptions: TestOptions.Regular10); + var comp = CreateCompilationWithMscorlibAndSpan(source, parseOptions: TestOptions.RegularDefault.WithLanguageVersion(langVersion)); comp.VerifyDiagnostics( // (8,20): error CS8157: Cannot return 'y' by reference because it was initialized to a value that cannot be returned by reference // return ref y; @@ -4574,26 +4576,13 @@ ref struct S // (16,24): error CS8157: Cannot return 'y' by reference because it was initialized to a value that cannot be returned by reference // return ref y; Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "y").WithArguments("y").WithLocation(16, 24)); - - comp = CreateCompilationWithMscorlibAndSpan(source); - comp.VerifyDiagnostics( - // (7,26): error CS8350: This combination of arguments to 'S.M(ref long)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope - // ref long y = ref receiver.M(ref x); - Diagnostic(ErrorCode.ERR_CallArgMixing, "receiver.M(ref x)").WithArguments("S.M(ref long)", "x").WithLocation(7, 26), - // (7,41): error CS8168: Cannot return local 'x' by reference because it is not a ref local - // ref long y = ref receiver.M(ref x); - Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(7, 41), - // (15,30): error CS8350: This combination of arguments to 'S.M(ref long)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope - // ref long y = ref receiver.M(ref x); - Diagnostic(ErrorCode.ERR_CallArgMixing, "receiver.M(ref x)").WithArguments("S.M(ref long)", "x").WithLocation(15, 30), - // (15,45): error CS8168: Cannot return local 'x' by reference because it is not a ref local - // ref long y = ref receiver.M(ref x); - Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(15, 45)); } - [Fact] + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] [WorkItem(27772, "https://github.com/dotnet/roslyn/issues/27772")] - public void RefReturnInvocationOfRefLikeTypeRefResult_Repro() + public void RefReturnInvocationOfRefLikeTypeRefResult_Repro(LanguageVersion langVersion) { string source = @" using System; @@ -4632,21 +4621,11 @@ ref struct S public ref long M(ref long x) => ref x; }"; - var comp = CreateCompilationWithMscorlibAndSpan(source, parseOptions: TestOptions.Regular10); + var comp = CreateCompilationWithMscorlibAndSpan(source, parseOptions: TestOptions.RegularDefault.WithLanguageVersion(langVersion)); comp.VerifyDiagnostics( // (19,18): error CS8157: Cannot return 'z' by reference because it was initialized to a value that cannot be returned by reference // return ref z; Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "z").WithArguments("z").WithLocation(19, 18)); - - // Breaking change in C#11: Instance method on ref struct instance may capture unscoped ref or in arguments. - comp = CreateCompilationWithMscorlibAndSpan(source); - comp.VerifyDiagnostics( - // (18,23): error CS8350: This combination of arguments to 'S.M(ref long)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope - // ref var z = ref receiver.M(ref y); - Diagnostic(ErrorCode.ERR_CallArgMixing, "receiver.M(ref y)").WithArguments("S.M(ref long)", "x").WithLocation(18, 23), - // (18,38): error CS8157: Cannot return 'y' by reference because it was initialized to a value that cannot be returned by reference - // ref var z = ref receiver.M(ref y); - Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "y").WithArguments("y").WithLocation(18, 38)); } [Fact, WorkItem(49617, "https://github.com/dotnet/roslyn/issues/49617")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs index a4185f377137b..3d89f9b76aae1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs @@ -1626,11 +1626,13 @@ static void M1(Span s1) 2 - 1"); } - [Fact] + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] [WorkItem(27357, "https://github.com/dotnet/roslyn/issues/27357")] - public void PassingSpansToParameters_Errors() + public void PassingSpansToParameters_Errors(LanguageVersion languageVersion) { - CreateCompilationWithMscorlibAndSpan(@" + var comp = CreateCompilationWithMscorlibAndSpan(@" using System; class C { @@ -1654,51 +1656,83 @@ static void M1(Span s1) M2(y: out s2, x: ref s1); // five M2(y: out s1, x: ref s2); // six - M2(ref s1, out s1); // should be ok - M2(ref s2, out s2); // should be ok + M2(ref s1, out s1); // okay + M2(ref s2, out s2); // okay } - static void M2(ref Span x, out Span y) + static void M2(scoped ref Span x, out Span y) { y = default; } -}").VerifyDiagnostics( - // (16,24): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope - // M2(ref s1, out s2); // one - Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(16, 24), - // (16,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope - // M2(ref s1, out s2); // one - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s1, out s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "y").WithLocation(16, 9), - // (17,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope - // M2(ref s2, out s1); // two - Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(17, 16), - // (17,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope - // M2(ref s2, out s1); // two - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(17, 9), - // (19,24): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope - // M2(ref s1, out s2); // three - Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(19, 24), - // (19,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope - // M2(ref s1, out s2); // three - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s1, out s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "y").WithLocation(19, 9), - // (20,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope - // M2(ref s2, out s1); // four - Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(20, 16), - // (20,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope - // M2(ref s2, out s1); // four - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(20, 9), - // (22,19): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope - // M2(y: out s2, x: ref s1); // five - Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(22, 19), - // (22,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope - // M2(y: out s2, x: ref s1); // five - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s2, x: ref s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "y").WithLocation(22, 9), - // (23,30): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope - // M2(y: out s1, x: ref s2); // six - Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(23, 30), - // (23,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope - // M2(y: out s1, x: ref s2); // six - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s1, x: ref s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(23, 9)); +}", parseOptions: TestOptions.RegularDefault.WithLanguageVersion(languageVersion)); + + if (languageVersion == LanguageVersion.CSharp10) + { + // In C# 7.2 the input to an `out` parameter can escape which means several + // of the tests are errors due to stack copying to escape + comp.VerifyDiagnostics( + // (16,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope + // M2(ref s1, out s2); // one + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s1, out s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "y").WithLocation(16, 9), + // (16,24): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // M2(ref s1, out s2); // one + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(16, 24), + // (17,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // M2(ref s2, out s1); // two + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(17, 9), + // (17,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // M2(ref s2, out s1); // two + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(17, 16), + // (19,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope + // M2(ref s1, out s2); // three + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s1, out s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "y").WithLocation(19, 9), + // (19,24): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // M2(ref s1, out s2); // three + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(19, 24), + // (20,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // M2(ref s2, out s1); // four + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(20, 9), + // (20,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // M2(ref s2, out s1); // four + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(20, 16), + // (22,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope + // M2(y: out s2, x: ref s1); // five + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s2, x: ref s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "y").WithLocation(22, 9), + // (22,19): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // M2(y: out s2, x: ref s1); // five + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(22, 19), + // (23,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // M2(y: out s1, x: ref s2); // six + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s1, x: ref s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(23, 9), + // (23,30): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // M2(y: out s1, x: ref s2); // six + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(23, 30), + // (29,20): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // static void M2(scoped ref Span x, out Span y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(29, 20)); + } + else + { + comp.VerifyDiagnostics( + // (17,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // M2(ref s2, out s1); // two + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(17, 9), + // (17,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // M2(ref s2, out s1); // two + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(17, 16), + // (20,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // M2(ref s2, out s1); // four + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(20, 9), + // (20,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // M2(ref s2, out s1); // four + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(20, 16), + // (23,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // M2(y: out s1, x: ref s2); // six + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s1, x: ref s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(23, 9), + // (23,30): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // M2(y: out s1, x: ref s2); // six + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(23, 30)); + } } [Fact]