diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index bb951edf8d055..8ded8764ef004 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -22,17 +22,20 @@ internal partial class Binder /// /// 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. - /// The 0 and 1 are special scopes - - /// 0 is the "external" or "return" scope that is outside of the containing method/lambda. - /// If something can escape to scope 0, it can escape to any scope in a given method or can be returned. - /// 1 is the "parameter" or "top" scope that is just inside the containing method/lambda. + /// 0, 1 and 2 are special scopes - + /// 0 is the "calling method" scope that is outside of the containing method/lambda. + /// If something can escape to scope 0, it can escape to any scope in a given method through a ref parameter or return. + /// 1 is the "return-only" scope that is outside of the containing method/lambda. + /// If something can escape to scope 1, it can escape to any scope in a given method or can be returned, but it can't escape through a ref parameter. + /// 2 is the "current method" scope that is just inside the containing method/lambda. /// If something can escape to scope 1, it can escape to any scope in a given method, but cannot be returned. /// n + 1 corresponds to scopes immediately inside a scope of depth n. /// Since sibling scopes do not intersect and a value cannot escape from one to another without /// escaping to a wider scope, we can use simple depth numbering without ambiguity. /// internal const uint ExternalScope = 0; - internal const uint TopLevelScope = 1; + internal const uint ReturnOnlyScope = 1; + internal const uint TopLevelScope = 2; // Some value kinds are semantically the same and the only distinction is how errors are reported // for those purposes we reserve lowest 2 bits @@ -719,7 +722,7 @@ private static bool CheckLocalRefEscape(SyntaxNode node, BoundLocal local, uint return true; } - if (escapeTo == Binder.ExternalScope) + if (escapeTo is Binder.ExternalScope or Binder.ReturnOnlyScope) { if (localSymbol.RefKind == RefKind.None) { @@ -794,21 +797,17 @@ private uint GetParameterValEscape(ParameterSymbol parameter) private uint GetParameterRefEscape(ParameterSymbol parameter) { - if (UseUpdatedEscapeRules) - { - return parameter.RefKind is RefKind.None || parameter.EffectiveScope != DeclarationScope.Unscoped ? Binder.TopLevelScope : Binder.ExternalScope; - } - else + return parameter switch { - // byval parameters can escape to method's top level. Others can escape further. - // NOTE: "method" here means nearest containing method, lambda or local function. - return parameter.RefKind == RefKind.None ? Binder.TopLevelScope : Binder.ExternalScope; - } + { RefKind: RefKind.None } or { EffectiveScope: not DeclarationScope.Unscoped } => Binder.TopLevelScope, + { Type.IsRefLikeType: true } => Binder.ReturnOnlyScope, + _ => Binder.ExternalScope + }; } private bool CheckParameterValEscape(SyntaxNode node, BoundParameter parameter, uint escapeTo, BindingDiagnosticBag diagnostics) { - Debug.Assert(escapeTo == Binder.ExternalScope); + Debug.Assert(escapeTo is Binder.ExternalScope or Binder.ReturnOnlyScope); if (UseUpdatedEscapeRules) { var parameterSymbol = parameter.ParameterSymbol; @@ -826,22 +825,32 @@ private bool CheckParameterValEscape(SyntaxNode node, BoundParameter parameter, } } - private bool CheckParameterRefEscape(SyntaxNode node, BoundParameter parameter, uint escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) + private bool CheckParameterRefEscape(SyntaxNode node, BoundExpression parameter, ParameterSymbol parameterSymbol, uint escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) { - var parameterSymbol = parameter.ParameterSymbol; - - if (GetParameterRefEscape(parameterSymbol) > escapeTo) + var refSafeToEscape = GetParameterRefEscape(parameterSymbol); + if (refSafeToEscape > escapeTo) { var isRefScoped = parameterSymbol.EffectiveScope == DeclarationScope.RefScoped; - Debug.Assert(parameterSymbol.RefKind == RefKind.None || isRefScoped); - if (checkingReceiver) + Debug.Assert(parameterSymbol.RefKind == RefKind.None || isRefScoped || refSafeToEscape == Binder.ReturnOnlyScope); + + if (parameter is BoundThisReference) { - Error(diagnostics, isRefScoped ? ErrorCode.ERR_RefReturnScopedParameter2 : ErrorCode.ERR_RefReturnParameter2, parameter.Syntax, parameterSymbol.Name); + Error(diagnostics, ErrorCode.ERR_RefReturnStructThis, node); + return false; } - else + +#pragma warning disable format + var (errorCode, syntax) = (checkingReceiver, isRefScoped, refSafeToEscape) switch { - Error(diagnostics, isRefScoped ? ErrorCode.ERR_RefReturnScopedParameter : ErrorCode.ERR_RefReturnParameter, node, parameterSymbol.Name); - } + (checkingReceiver: true, isRefScoped: true, _) => (ErrorCode.ERR_RefReturnScopedParameter2, parameter.Syntax), + (checkingReceiver: true, isRefScoped: false, Binder.ReturnOnlyScope) => (ErrorCode.ERR_RefReturnOnlyParameter, parameter.Syntax), + (checkingReceiver: true, isRefScoped: false, _) => (ErrorCode.ERR_RefReturnParameter2, parameter.Syntax), + (checkingReceiver: false, isRefScoped: true, _) => (ErrorCode.ERR_RefReturnScopedParameter, node), + (checkingReceiver: false, isRefScoped: false, Binder.ReturnOnlyScope) => (ErrorCode.ERR_RefReturnOnlyParameter, node), + (checkingReceiver: false, isRefScoped: false, _) => (ErrorCode.ERR_RefReturnParameter, node) + }; +#pragma warning restore format + Error(diagnostics, errorCode, syntax, parameterSymbol.Name); return false; } @@ -2362,7 +2371,7 @@ private static ErrorCode GetStandardLvalueError(BindValueKind kind) private static ErrorCode GetStandardRValueRefEscapeError(uint escapeTo) { - if (escapeTo == Binder.ExternalScope) + if (escapeTo is Binder.ExternalScope or Binder.ReturnOnlyScope) { return ErrorCode.ERR_RefReturnLvalueExpected; } @@ -2524,28 +2533,9 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return ((BoundLocal)expr).LocalSymbol.RefEscapeScope; case BoundKind.ThisReference: - Debug.Assert(this.ContainingMember() is MethodSymbol { ThisParameter: not null }); - - var thisref = (BoundThisReference)expr; - - // "this" is an RValue, unless in a struct. - if (!thisref.Type.IsValueType) - { - break; - } - - if (UseUpdatedEscapeRules) - { - if (this.ContainingMember() is MethodSymbol { ThisParameter: var thisParameter } && - thisParameter.EffectiveScope == DeclarationScope.Unscoped) - { - return Binder.ExternalScope; - } - } - - //"this" is not returnable by reference in a struct. - // can ref escape to any other level - return Binder.TopLevelScope; + var thisParam = ((MethodSymbol)this.ContainingMember()).ThisParameter; + Debug.Assert(thisParam.Type.Equals(((BoundThisReference)expr).Type, TypeCompareKind.ConsiderEverything)); + return GetParameterRefEscape(thisParam); case BoundKind.ConditionalOperator: var conditional = (BoundConditionalOperator)expr; @@ -2761,7 +2751,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.RefValueOperator: // The undocumented __refvalue(tr, T) expression results in an lvalue of type T. // for compat reasons it is not ref-returnable (since TypedReference is not val-returnable) - if (escapeTo == Binder.ExternalScope) + if (escapeTo is Binder.ExternalScope or Binder.ReturnOnlyScope) { break; } @@ -2782,41 +2772,16 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.Parameter: var parameter = (BoundParameter)expr; - return CheckParameterRefEscape(node, parameter, escapeTo, checkingReceiver, diagnostics); + return CheckParameterRefEscape(node, parameter, parameter.ParameterSymbol, escapeTo, checkingReceiver, diagnostics); case BoundKind.Local: var local = (BoundLocal)expr; return CheckLocalRefEscape(node, local, escapeTo, checkingReceiver, diagnostics); case BoundKind.ThisReference: - Debug.Assert(this.ContainingMember() is MethodSymbol { ThisParameter: not null }); - - var thisref = (BoundThisReference)expr; - - // "this" is an RValue, unless in a struct. - if (!thisref.Type.IsValueType) - { - break; - } - - //"this" is not returnable by reference in a struct. - if (escapeTo == Binder.ExternalScope) - { - if (UseUpdatedEscapeRules) - { - if (this.ContainingMember() is MethodSymbol { ThisParameter: var thisParameter } && - thisParameter.EffectiveScope == DeclarationScope.Unscoped) - { - // can ref escape to any other level - return true; - } - } - Error(diagnostics, ErrorCode.ERR_RefReturnStructThis, node); - return false; - } - - // can ref escape to any other level - return true; + var thisParam = ((MethodSymbol)this.ContainingMember()).ThisParameter; + Debug.Assert(thisParam.Type.Equals(((BoundThisReference)expr).Type, TypeCompareKind.ConsiderEverything)); + return CheckParameterRefEscape(node, expr, thisParam, escapeTo, checkingReceiver, diagnostics); case BoundKind.ConditionalOperator: var conditional = (BoundConditionalOperator)expr; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 15e8ca841f1b2..566008ea46a62 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -237,7 +237,7 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi BoundExpression argument = (node.Expression == null) ? BadExpression(node).MakeCompilerGenerated() : BindValue(node.Expression, diagnostics, BindValueKind.RValue); - argument = ValidateEscape(argument, ExternalScope, isByRef: false, diagnostics: diagnostics); + argument = ValidateEscape(argument, ReturnOnlyScope, isByRef: false, diagnostics: diagnostics); if (!argument.HasAnyErrors) { @@ -1546,7 +1546,7 @@ private BoundAssignmentOperator BindAssignment( var rightEscape = GetRefEscape(op2, LocalScopeDepth); if (leftEscape < rightEscape) { - Error(diagnostics, ErrorCode.ERR_RefAssignNarrower, node, getName(op1), op2.Syntax); + Error(diagnostics, rightEscape == Binder.ReturnOnlyScope ? ErrorCode.ERR_RefAssignReturnOnly : ErrorCode.ERR_RefAssignNarrower, node, getName(op1), op2.Syntax); op2 = ToBadExpression(op2); } } @@ -2979,7 +2979,7 @@ private BoundStatement BindReturn(ReturnStatementSyntax syntax, BindingDiagnosti else { arg = CreateReturnConversion(syntax, diagnostics, arg, sigRefKind, retType); - arg = ValidateEscape(arg, Binder.ExternalScope, refKind != RefKind.None, diagnostics); + arg = ValidateEscape(arg, Binder.ReturnOnlyScope, refKind != RefKind.None, diagnostics); } } } @@ -3421,7 +3421,7 @@ static BoundBlock bindExpressionBodyAsBlockInternal(ArrowExpressionClauseSyntax ExpressionSyntax expressionSyntax = expressionBody.Expression.CheckAndUnwrapRefExpression(diagnostics, out refKind); BindValueKind requiredValueKind = bodyBinder.GetRequiredReturnValueKind(refKind); BoundExpression expression = bodyBinder.BindValue(expressionSyntax, diagnostics, requiredValueKind); - expression = bodyBinder.ValidateEscape(expression, Binder.ExternalScope, refKind != RefKind.None, diagnostics); + expression = bodyBinder.ValidateEscape(expression, Binder.ReturnOnlyScope, refKind != RefKind.None, diagnostics); return bodyBinder.CreateBlockFromExpression(expressionBody, bodyBinder.GetDeclaredLocalsForScope(expressionBody), refKind, expression, expressionSyntax, diagnostics); } @@ -3439,7 +3439,7 @@ public BoundBlock BindLambdaExpressionAsBlock(ExpressionSyntax body, BindingDiag var expressionSyntax = body.CheckAndUnwrapRefExpression(diagnostics, out refKind); BindValueKind requiredValueKind = GetRequiredReturnValueKind(refKind); BoundExpression expression = bodyBinder.BindValue(expressionSyntax, diagnostics, requiredValueKind); - expression = ValidateEscape(expression, Binder.ExternalScope, refKind != RefKind.None, diagnostics); + expression = ValidateEscape(expression, Binder.ReturnOnlyScope, refKind != RefKind.None, diagnostics); return bodyBinder.CreateBlockFromExpression(body, bodyBinder.GetDeclaredLocalsForScope(body), refKind, expression, expressionSyntax, diagnostics); } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 9a0f54618eb72..04bff9bc269d8 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5077,6 +5077,12 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot return by reference a member of parameter '{0}' because it is scoped to the current method + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return local '{0}' by reference because it is not a ref local @@ -5791,6 +5797,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + enum generic type constraints diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 5929fe5682ba6..d49449df1f81b 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2124,7 +2124,10 @@ internal enum ErrorCode WRN_ScopedMismatchInParameterOfTarget = 9073, WRN_ScopedMismatchInParameterOfOverrideOrImplementation = 9074, ERR_RefReturnScopedParameter = 9075, - ERR_RefReturnScopedParameter2 = 9076 + ERR_RefReturnScopedParameter2 = 9076, + ERR_RefReturnOnlyParameter = 9077, + ERR_RefReturnOnlyParameter2 = 9078, + ERR_RefAssignReturnOnly = 9079, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index c8ef49488ffe4..f9d9b37730345 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2226,6 +2226,9 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.WRN_ScopedMismatchInParameterOfOverrideOrImplementation: case ErrorCode.ERR_RefReturnScopedParameter: case ErrorCode.ERR_RefReturnScopedParameter2: + case ErrorCode.ERR_RefReturnOnlyParameter: + case ErrorCode.ERR_RefReturnOnlyParameter2: + case ErrorCode.ERR_RefAssignReturnOnly: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 8de91af2e61fa..c68eb4d8c7cbf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1287,6 +1287,11 @@ Přiřazení odkazu {1} k {0} nelze provést, protože {1} má užší řídicí obor než {0}. + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. Pole ref nemůže odkazovat na hodnotu ref struct. @@ -1302,6 +1307,16 @@ Levá strana přiřazení odkazu musí být parametr Ref. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 2a9b54ddc616d..8692d8632c0c5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1287,6 +1287,11 @@ ref-assign von "{1}" zu "{0}" ist nicht möglich, weil "{1}" einen geringeren Escapebereich als "{0}" aufweist. + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. Ein Ref-Feld kann nicht auf eine Ref-Struktur verweisen. @@ -1302,6 +1307,16 @@ Die linke Seite einer Ref-Zuweisung muss eine Ref-Variable sein. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index ed600f1473222..b02a146c93efb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1287,6 +1287,11 @@ No se puede asignar referencia "{1}" a "{0}" porque "{1}" tiene un ámbito de escape más limitado que "{0}". + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. Un campo de referencia no puede hacer referencia a una estructura de referencia. @@ -1302,6 +1307,16 @@ La parte izquierda de una asignación de referencias debe ser una variable local. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 4b6836cfc672f..aba5fc45e761f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1287,6 +1287,11 @@ Impossible d'effectuer une assignation par référence de '{1}' vers '{0}', car '{1}' a une portée de sortie plus limitée que '{0}'. + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. Un champ ref ne peut pas faire référence à un struct ref. @@ -1302,6 +1307,16 @@ Le côté gauche d’une affectation ref doit être une variable ref. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index ac12d777d5134..c75a8e92291a4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1287,6 +1287,11 @@ Non è possibile assegnare '{1}' a '{0}' come ref perché l'ambito di escape di '{1}' è ridotto rispetto a quello di '{0}'. + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. Un campo ref non può fare riferimento a un ref struct. @@ -1302,6 +1307,16 @@ La parte sinistra di un'assegnazione ref deve essere una variabile ref. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index ec7eee3d8caf2..65e1f9e213d31 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1287,6 +1287,11 @@ '{1}' を '{0}' に ref 割り当てすることはできません。'{1}' のエスケープ スコープが '{0}' より狭いためです。 + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. ref フィールドは ref 構造体を参照できません。 @@ -1302,6 +1307,16 @@ ref 代入の左辺は ref 変数である必要があります。 + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 8f768e0cba094..ff234ed42ab32 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1287,6 +1287,11 @@ '{1}'을(를) '{0}'에 참조 할당할 수 없습니다. '{1}'이(가) '{0}'보다 이스케이프 범위가 좁기 때문입니다. + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. ref 필드는 ref 구조체를 참조할 수 없습니다. @@ -1302,6 +1307,16 @@ ref 할당의 왼쪽은 ref 변수여야 합니다. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 03d6290263f95..3c9b7611cbe47 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1287,6 +1287,11 @@ Nie można przypisać odwołania elementu „{1}” do elementu „{0}”, ponieważ element „{1}” ma węższy zakres wyjścia niż element „{0}”. + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. Pole referencyjne nie może odwoływać się do struktury referencyjnej. @@ -1302,6 +1307,16 @@ Lewa strona przypisania referencyjnego musi być zmienną referencyjną. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 100bc9f3b1f83..63c6224abaa39 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1287,6 +1287,11 @@ Não é possível atribuir ref '{1}' a '{0}' porque '{1}' tem um escopo de escape mais limitado que '{0}'. + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. Um campo ref não pode se referir a um struct ref. @@ -1302,6 +1307,16 @@ O lado esquerdo de uma atribuição ref deve ser uma variável ref. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 250791c2a3fd6..de1fb4d926785 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1287,6 +1287,11 @@ Не удается присвоить по ссылке "{1}" для "{0}", так как escape-область у "{1}" уже, чем у "{0}". + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. Поле ref не должно ссылаться на ref struct. @@ -1302,6 +1307,16 @@ Левая сторона назначения ref должна быть переменной ref. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index d7cc5e3b64575..bd7101a513967 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1287,6 +1287,11 @@ '{1}', '{0}' öğesinden daha dar bir kaçış kapsamı içerdiğinden '{0}' öğesine '{1}' ref ataması yapılamıyor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. Başvuru alanı bir başvuru yapısına başvuramaz. @@ -1302,6 +1307,16 @@ ref atamasının sol tarafı, ref değişkeni olmalıdır. + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 0e9a1a4ded4c8..4fff2340c997b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1287,6 +1287,11 @@ 无法将“{1}”重新赋值为“{0}”,因为“{1}”具有比“{0}”更窄的转义范围。 + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. ref 字段不能引用 ref 结构。 @@ -1302,6 +1307,16 @@ ref 赋值的左侧必须为 ref 变量。 + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index fa11c82ad73b3..23e75df9fe506 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1287,6 +1287,11 @@ 不能將 '{1}' 參考指派至 '{0}',因為 '{1}' 的逸出範圍比 '{0}' 還要窄。 + + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + Cannot ref-assign '{1}' to '{0}' because '{1}' can only escape the current method through a return statement. + + A ref field cannot refer to a ref struct. ref 欄位不能參考 ref 結構。 @@ -1302,6 +1307,16 @@ 參考指派的左側必須為 ref 變數。 + + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return a parameter by reference '{0}' through a ref parameter; it can only be returned in a return statement + + + + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + Cannot return by reference a member of parameter '{0}' through a ref parameter; it can only be returned in a return statement + + Cannot return a parameter by reference '{0}' because it is scoped to the current method Cannot return a parameter by reference '{0}' because it is scoped to the current method diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 0acb2966d3742..a7a24718b7a73 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -1124,7 +1124,10 @@ static void F([UnscopedRef] ref R1 r1) comp.VerifyEmitDiagnostics( // (8,12): error CS9050: A ref field cannot refer to a ref struct. // public ref R1 F; - Diagnostic(ErrorCode.ERR_RefFieldCannotReferToRefStruct, "ref R1").WithLocation(8, 12)); + Diagnostic(ErrorCode.ERR_RefFieldCannotReferToRefStruct, "ref R1").WithLocation(8, 12), + // (15,9): error CS9079: Cannot ref-assign 'r1' to 'F' because 'r1' can only escape the current method through a return statement. + // r2.F = ref r1; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "r2.F = ref r1").WithArguments("F", "r1").WithLocation(15, 9)); } /// @@ -2980,7 +2983,7 @@ static void F1(ref R a, __arglist) { } } [Fact] - public void MethodArgumentsMustMatch_07() + public void MethodArgumentsMustMatch_07_1() { var source = @"using System.Diagnostics.CodeAnalysis; @@ -2993,30 +2996,143 @@ static void F1(ref R a, __arglist) { } static void F00(ref R x, ref R y) { F0(__arglist(ref x, ref y)); } // 1 static void F01(ref R x, ref R y) { F1(ref x, __arglist(ref y)); } // 2 static void F20(ref R x, [UnscopedRef] ref R y) { F0(__arglist(ref x, ref y)); } // 3 - static void F21(ref R x, [UnscopedRef] ref R y) { F1(ref x, __arglist(ref y)); } - static void F50([UnscopedRef] ref R x, [UnscopedRef] ref R y) { F0(__arglist(ref x, ref y)); } - static void F51([UnscopedRef] ref R x, [UnscopedRef] ref R y) { F1(ref x, __arglist(ref y)); } + static void F21(ref R x, [UnscopedRef] ref R y) { F1(ref x, __arglist(ref y)); } // 4 + static void F50([UnscopedRef] ref R x, [UnscopedRef] ref R y) { F0(__arglist(ref x, ref y)); } // 5 + static void F51([UnscopedRef] ref R x, [UnscopedRef] ref R y) { F1(ref x, __arglist(ref y)); } // 6 }"; var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); comp.VerifyEmitDiagnostics( - // (8,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(8, 41), // (8,58): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method // static void F00(ref R x, ref R y) { F0(__arglist(ref x, ref y)); } // 1 Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(8, 58), - // (9,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(9, 41), + // (8,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(8, 41), // (9,65): error CS9075: Cannot return a parameter by reference 'y' because it is scoped to the current method // static void F01(ref R x, ref R y) { F1(ref x, __arglist(ref y)); } // 2 Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "y").WithArguments("y").WithLocation(9, 65), + // (9,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(9, 41), + // (10,72): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method + // static void F20(ref R x, [UnscopedRef] ref R y) { F0(__arglist(ref x, ref y)); } // 3 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(10, 72), // (10,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 F20(ref R x, [UnscopedRef] 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, 55), - // (10,72): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method - // static void F20(ref R x, [UnscopedRef] ref R y) { F0(__arglist(ref x, ref y)); } // 3 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(10, 72)); + // (11,79): 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(ref R x, [UnscopedRef] ref R y) { F1(ref x, __arglist(ref y)); } // 4 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "y").WithArguments("y").WithLocation(11, 79), + // (11,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 F21(ref R x, [UnscopedRef] 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, 55), + // (12,86): 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([UnscopedRef] ref R x, [UnscopedRef] ref R y) { F0(__arglist(ref x, ref y)); } // 5 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "x").WithArguments("x").WithLocation(12, 86), + // (12,69): 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([UnscopedRef] ref R x, [UnscopedRef] 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, 69), + // (13,93): 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([UnscopedRef] ref R x, [UnscopedRef] ref R y) { F1(ref x, __arglist(ref y)); } // 6 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "y").WithArguments("y").WithLocation(13, 93), + // (13,69): 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([UnscopedRef] ref R x, [UnscopedRef] 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, 69)); + } + + [Fact] + public void MethodArgumentsMustMatch_07_2() + { + var source = +@" +using System.Diagnostics.CodeAnalysis; +using System; + +ref struct R +{ + public byte B; + public ref byte RB; +} + +class Program +{ + static R F0(__arglist) + { + var args = new ArgIterator(__arglist); + ref R r = ref __refvalue(args.GetNextArg(), R); + r.RB = ref r.B; // 1 + return r; + } + + static void F1(ref R y) { F0(__arglist(ref y)); } // 2 + static void F2([UnscopedRef] ref R y) { F0(__arglist(ref y)); } // 3 + + static R F3(ref R y) { return F0(__arglist(ref y)); } // 4 + static R F4([UnscopedRef] ref R y) { return F0(__arglist(ref y)); } // 5 +}"; + // __refvalue operators can only ref-escape to current method. + // The __arglist operator here assumes that `F0` could do `y.RB = ref y`. + // These assumptions are contradictory, but it just ends up being more strict in both directions. + // Tracking in https://github.com/dotnet/roslyn/issues/64130 + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + 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,48): error CS9075: Cannot return a parameter by reference 'y' because it is scoped to the current method + // static void F1(ref R y) { F0(__arglist(ref y)); } // 2 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "y").WithArguments("y").WithLocation(21, 48), + // (21,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 F1(ref R y) { F0(__arglist(ref y)); } // 2 + Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(21, 31), + // (22,62): 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([UnscopedRef] ref R y) { F0(__arglist(ref y)); } // 3 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "y").WithArguments("y").WithLocation(22, 62), + // (22,45): 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([UnscopedRef] ref R y) { F0(__arglist(ref y)); } // 3 + Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(22, 45), + // (24,52): error CS9075: Cannot return a parameter by reference 'y' because it is scoped to the current method + // static R F3(ref R y) { return F0(__arglist(ref y)); } // 4 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "y").WithArguments("y").WithLocation(24, 52), + // (24,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 F3(ref R y) { return F0(__arglist(ref y)); } // 4 + Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(24, 35), + // (25,66): 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([UnscopedRef] ref R y) { return F0(__arglist(ref y)); } // 5 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "y").WithArguments("y").WithLocation(25, 66), + // (25,49): 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([UnscopedRef] ref R y) { return F0(__arglist(ref y)); } // 5 + Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref y))").WithArguments("Program.F0(__arglist)", "__arglist").WithLocation(25, 49)); + } + + [Fact] + public void MethodArgumentsMustMatch_07_3() + { + // demonstrate the non-ref-fields behavior. + var source = +@" +using System; + +class Program +{ + static ref int F0(__arglist) + { + var args = new ArgIterator(__arglist); + ref int r = ref __refvalue(args.GetNextArg(), int); + return ref r; // 1 + } + + static void F1(scoped ref int y) { F0(__arglist(ref y)); } + static void F2(ref int y) { F0(__arglist(ref y)); } + + static ref int F3(scoped ref int y) { return ref F0(__arglist(ref y)); } + static ref int F4(ref int y) { return ref F0(__arglist(ref y)); } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyEmitDiagnostics( + // (10,20): error CS8157: Cannot return 'r' by reference because it was initialized to a value that cannot be returned by reference + // return ref r; // 1 + Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "r").WithArguments("r").WithLocation(10, 20)); } /// @@ -6349,8 +6465,11 @@ static T ReadIn(in R2 r2In) var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); 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)); + // public ref R1 R1; + Diagnostic(ErrorCode.ERR_RefFieldCannotReferToRefStruct, "ref R1").WithLocation(10, 12), + // (11,45): error CS9079: Cannot ref-assign 'r1' to 'R1' because 'r1' can only escape the current method through a return statement. + // public R2([UnscopedRef] ref R1 r1) { R1 = ref r1; } + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "R1 = ref r1").WithArguments("R1", "r1").WithLocation(11, 45)); } [Fact] @@ -7118,7 +7237,10 @@ static void F([UnscopedRef] ref R r) Diagnostic(ErrorCode.ERR_RefFieldCannotReferToRefStruct, "ref R").WithLocation(5, 12), // (5,21): error CS0523: Struct member 'R.Next' of type 'R' causes a cycle in the struct layout // public ref R Next; - Diagnostic(ErrorCode.ERR_StructLayoutCycle, "Next").WithArguments("R.Next", "R").WithLocation(5, 21)); + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "Next").WithArguments("R.Next", "R").WithLocation(5, 21), + // (11,9): error CS9079: Cannot ref-assign 'r' to 'Next' because 'r' can only escape the current method through a return statement. + // r.Next = ref r; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "r.Next = ref r").WithArguments("Next", "r").WithLocation(11, 9)); } /// @@ -13120,6 +13242,57 @@ public void ReturnThis_02(LanguageVersion languageVersion) Diagnostic(ErrorCode.ERR_RefReturnStructThis, "this").WithLocation(5, 32)); } + [Fact, WorkItem(63526, "https://github.com/dotnet/roslyn/issues/63526")] + public void ReturnThis_03() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + ref struct S2 { + public S S; + } + + ref struct S { + public int field; + public ref int refField; + + ref int Prop1 => ref field; // 1 + + [UnscopedRef] ref int Prop2 => ref field; // okay + + S2 Prop3 => new S2 { S = this }; // Okay + + S Prop4 => new S { refField = ref this.field }; // 2 + + [UnscopedRef] S Prop5 => new S { refField = ref this.field }; // okay + + S M1() => new S { refField = ref this.field }; // 3 + + [UnscopedRef] + S M2() => new S { refField = ref this.field }; // okay + + static S M3(ref S s) => new S { refField = ref s.field }; // 4 + + static S M4([UnscopedRef] ref S s) => new S { refField = ref s.field }; // okay + } + """; + + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics( + // (11,26): error CS8170: Struct members cannot return 'this' or other instance members by reference + // ref int Prop1 => ref field; // 1 + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "field").WithLocation(11, 26), + // (17,24): error CS8170: Struct members cannot return 'this' or other instance members by reference + // S Prop4 => new S { refField = ref this.field }; // 2 + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "refField = ref this.field").WithLocation(17, 24), + // (21,23): error CS8170: Struct members cannot return 'this' or other instance members by reference + // S M1() => new S { refField = ref this.field }; // 3 + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "refField = ref this.field").WithLocation(21, 23), + // (26,52): error CS9076: Cannot return by reference a member of parameter 's' because it is scoped to the current method + // static S M3(ref S s) => new S { refField = ref s.field }; // 4 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter2, "s").WithArguments("s").WithLocation(26, 52)); + } + [Fact] public void RefInitializer_LangVer() { @@ -17882,16 +18055,12 @@ static void M12(ref ByteContainer bc) static void M21([UnscopedRef] ref ByteContainer bc, ref RefByteContainer rbc) { // error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter. - // this error will be enabled in a future PR before RTM. - // https://github.com/dotnet/csharplang/pull/6450 - rbc = bc.ByteRef; + rbc = bc.ByteRef; // 1 } static void M22([UnscopedRef] ref ByteContainer bc, ref RefByteContainer rbc) { // error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter. - // this error will be enabled in a future PR before RTM. - // https://github.com/dotnet/csharplang/pull/6450 - rbc = bc.GetByteRef(); + rbc = bc.GetByteRef(); // 2 } static RefByteContainer M31([UnscopedRef] ref ByteContainer bc) @@ -17904,22 +18073,311 @@ static RefByteContainer M32([UnscopedRef] ref ByteContainer bc) static RefByteContainer M41(ref ByteContainer bc) // error: `bc.ByteRef` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod. - => bc.ByteRef; + => bc.ByteRef; // 3 static RefByteContainer M42(ref ByteContainer bc) // error: `bc.GetByteRef()` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod. - => bc.GetByteRef(); + => bc.GetByteRef(); // 4 } """; var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); comp.VerifyDiagnostics( + // (42,15): error CS9077: Cannot return a parameter by reference 'bc' through a ref parameter; it can only be returned in a return statement + // rbc = bc.ByteRef; // 1 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "bc").WithArguments("bc").WithLocation(42, 15), + // (47,15): error CS9077: Cannot return a parameter by reference 'bc' through a ref parameter; it can only be returned in a return statement + // rbc = bc.GetByteRef(); // 2 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "bc").WithArguments("bc").WithLocation(47, 15), + // (60,12): error CS9075: Cannot return a parameter by reference 'bc' because it is scoped to the current method + // => bc.ByteRef; // 3 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "bc").WithArguments("bc").WithLocation(60, 12), // (64,12): error CS9075: Cannot return a parameter by reference 'bc' because it is scoped to the current method - // => bc.ByteRef; - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "bc").WithArguments("bc").WithLocation(64, 12), - // (68,12): error CS9075: Cannot return a parameter by reference 'bc' because it is scoped to the current method - // => bc.GetByteRef(); - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "bc").WithArguments("bc").WithLocation(68, 12)); + // => bc.GetByteRef(); // 4 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "bc").WithArguments("bc").WithLocation(64, 12)); + } + + [Fact, WorkItem(63526, "https://github.com/dotnet/roslyn/issues/63526")] + public void ReturnOnlyScope_01() + { + // test that return scope is used in all return-ey locations. + var source = """ + using System.Diagnostics.CodeAnalysis; + + ref struct RS + { + public byte B; + + [UnscopedRef] + public RSOut ToRSOut() + { + return new RSOut { RB = ref this.B }; + } + } + + ref struct RSOut + { + public ref byte RB; + } + + class Program + { + RS M1([UnscopedRef] ref RS rs) => rs; + void M2([UnscopedRef] ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 1 + + RS M3([UnscopedRef] ref RS rs) + { + return rs; + } + void M4([UnscopedRef] ref RS rs, out RSOut rs1) + { + rs1 = rs.ToRSOut(); // 2 + } + + void localContainer() + { + #pragma warning disable 8321 + RS M1([UnscopedRef] ref RS rs) => rs; + void M2([UnscopedRef] ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 3 + + RS M3([UnscopedRef] ref RS rs) + { + return rs; + } + void M4([UnscopedRef] ref RS rs, out RSOut rs1) + { + rs1 = rs.ToRSOut(); // 4 + } + } + + delegate RS ReturnsRefStruct([UnscopedRef] ref RS rs); + delegate void RefStructOut([UnscopedRef] ref RS rs, out RSOut rs1); + + void lambdaContainer() + { + ReturnsRefStruct d1 = ([UnscopedRef] ref RS rs) => rs; + RefStructOut d2 = ([UnscopedRef] ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 5 + + ReturnsRefStruct d3 = ([UnscopedRef] ref RS rs) => + { + return rs; + }; + RefStructOut d4 = ([UnscopedRef] ref RS rs, out RSOut rs1) => + { + rs1 = rs.ToRSOut(); // 6 + }; + } + } + """; + + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics( + // (22,62): error CS9077: Cannot return a parameter by reference 'rs' through a ref parameter; it can only be returned in a return statement + // void M2([UnscopedRef] ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 1 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "rs").WithArguments("rs").WithLocation(22, 62), + // (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,66): error CS9077: Cannot return a parameter by reference 'rs' through a ref parameter; it can only be returned in a return statement + // void M2([UnscopedRef] ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 3 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "rs").WithArguments("rs").WithLocation(37, 66), + // (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,77): error CS9077: Cannot return a parameter by reference 'rs' through a ref parameter; it can only be returned in a return statement + // RefStructOut d2 = ([UnscopedRef] ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut(); // 5 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "rs").WithArguments("rs").WithLocation(55, 77), + // (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)); + } + + [Fact, WorkItem(63526, "https://github.com/dotnet/roslyn/issues/63526")] + public void ReturnOnlyScope_02() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + #pragma warning disable 8321 // unused local function + static bool condition() => false; + + static void M1(ref S p1, ref S p2) { + p2.refField = ref p1.field; // 1 + p2.refField = ref p1.refField; // Okay + } + + static void M2(ref S p1, out S p2) { + p2 = default; + p2.refField = ref p1.field; // 2 + p2.refField = ref p1.refField; // Okay + } + + static void M3(out S p1, ref S p2) { + p1 = default; + p2.refField = ref p1.field; // 3 + p2.refField = ref p1.refField; // Okay + } + + // 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 + } + + static void M5([UnscopedRef] ref S p1, ref S2 p2) { + p2 = Inner1(ref p1); // 5 + p2 = Inner2(ref p1); // Okay + } + + static void M6([UnscopedRef] ref S p1, out S2 p2) { + p2 = Inner1(ref p1); // 6 + p2 = Inner2(ref p1); // Okay + } + + static void M7(scoped ref S p1, ref S2 p2) { + p2 = Inner1(ref p1); // 7 + p2 = Inner2(ref p1); // Okay + } + + static S2 M8(scoped ref S p) { + if (condition()) return Inner1(ref p); // 8 + if (condition()) return Inner2(ref p); // Okay + + throw null!; + } + + static S2 M9([UnscopedRef] ref S p) { + if (condition()) return Inner1(ref p); // Okay + if (condition()) return Inner2(ref p); // Okay + + throw null!; + } + + static S2 Inner1([UnscopedRef] ref S s) => new S2 { S = s }; + static S2 Inner2(scoped ref S s) => new S2 { S = s }; + + ref struct S { + public int field; + public ref int refField; + } + + ref struct S2 { + public S S; + } + """; + + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics( + // (7,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; // 1 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "p2.refField = ref p1.field").WithArguments("refField", "p1.field").WithLocation(7, 5), + // (13,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; // 2 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "p2.refField = ref p1.field").WithArguments("refField", "p1.field").WithLocation(13, 5), + // (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), + // (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 + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "p2.refField = ref p1.field").WithArguments("refField", "p1.field").WithLocation(26, 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 + 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 + 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 + 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 + 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 + 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)); + } + + [Fact, WorkItem(63526, "https://github.com/dotnet/roslyn/issues/63526")] + public void ReturnOnlyScope_03() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + #pragma warning disable 8321 // unused local function + ref struct S2 { + public S S; + } + + ref struct S { + public int field; + public ref int refField; + + void M1(ref S p) { + p.refField = ref this.field; // 1 + p.refField = ref this.refField; // Okay + } + + [UnscopedRef] + void M2(ref S p) { + p.refField = ref this.field; // 2 + p.refField = ref this.refField; // Okay + } + + [UnscopedRef] + void M3(out S p) { + p = default; + p.refField = ref this.field; // 3 + p.refField = ref this.refField; // Okay + } + + void M4(ref S2 p) { + p = Inner1(ref this); // 4 + p = Inner2(ref this); // Okay + } + + void M5(out S2 p) { + p = Inner1(ref this); // 5 + p = Inner2(ref this); // Okay + } + + static S2 Inner1([UnscopedRef] ref S s) => new S2 { S = s }; + static S2 Inner2(scoped ref S s) => new S2 { S = s }; + } + """; + + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + comp.VerifyDiagnostics( + // (13,9): error CS8374: Cannot ref-assign 'this.field' to 'refField' because 'this.field' has a narrower escape scope than 'refField'. + // p.refField = ref this.field; // 1 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "p.refField = ref this.field").WithArguments("refField", "this.field").WithLocation(13, 9), + // (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), + // (31,24): error CS8170: Struct members cannot return 'this' or other instance members by reference + // p = Inner1(ref this); // 4 + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "this").WithLocation(31, 24), + // (36,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); // 5 + Diagnostic(ErrorCode.ERR_EscapeCall, "Inner1(ref this)").WithArguments("S.Inner1(ref S)", "s").WithLocation(36, 13), + // (36,24): error CS8170: Struct members cannot return 'this' or other instance members by reference + // p = Inner1(ref this); // 5 + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "this").WithLocation(36, 24)); } } }