From 2f6f90df1304a78e9f9d2edd49c3a0091f00af3f Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 24 Jul 2026 13:42:27 +0200 Subject: [PATCH 1/9] Unsafe evolution: allow await in unsafe context --- .../CSharp/Portable/Binder/BinderFlags.cs | 4 +- .../CSharp/Portable/Binder/Binder_Await.cs | 11 +- .../Portable/Binder/FixedStatementBinder.cs | 2 +- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../CSharp/Portable/Errors/ErrorFacts.cs | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Test/CSharp15/UnsafeEvolutionTests.cs | 223 ++++++++++++++++-- .../Emit/CodeGen/CodeGenAwaitForeachTests.cs | 2 +- .../Emit/CodeGen/CodeGenAwaitUsingTests.cs | 2 +- .../Emit3/RefUnsafeInIteratorAndAsyncTests.cs | 12 +- .../Semantic/Semantics/BindingAsyncTests.cs | 21 +- .../Semantic/Semantics/LocalFunctionTests.cs | 20 +- .../Semantics/StackAllocInitializerTests.cs | 2 +- 26 files changed, 304 insertions(+), 65 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs index a42e86e1edea0..f54662cdf0a80 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs @@ -19,7 +19,7 @@ internal enum BinderFlags : uint SuppressObsoleteChecks = 1 << 1, ConstructorInitializer = 1 << 2, FieldInitializer = 1 << 3, - // Previously used, can be reused in the future + InFixedStatement = 1 << 4, // body or declaration of a 'fixed' statement (used to disallow 'await') CollectionInitializerAddMethod = 1 << 5, // used for collection initializer add method overload resolution diagnostics AttributeArgument = 1 << 6, GenericConstraintsClause = 1 << 7, // "where" clause (used for cycle checking) @@ -120,6 +120,6 @@ internal enum BinderFlags : uint // Groups - AllClearedAtExecutableCodeBoundary = InLockBody | InCatchBlock | InCatchFilter | InFinallyBlock | InTryBlockOfTryCatch | InNestedFinallyBlock, + AllClearedAtExecutableCodeBoundary = InLockBody | InCatchBlock | InCatchFilter | InFinallyBlock | InTryBlockOfTryCatch | InNestedFinallyBlock | InFixedStatement, } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index 9a149731d33b9..704044b853e91 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -218,11 +218,20 @@ private bool ReportBadAwaitWithoutAsync(SyntaxNodeOrToken nodeOrToken, BindingDi /// True if errors were found. private bool ReportBadAwaitContext(SyntaxNodeOrToken nodeOrToken, BindingDiagnosticBag diagnostics) { - if (this.InUnsafeRegion && !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext)) + if (this.InUnsafeRegion && + !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext) && + !Compilation.IsFeatureEnabled(MessageID.IDS_FeatureUnsafeEvolution)) { Error(diagnostics, ErrorCode.ERR_AwaitInUnsafeContext, nodeOrToken.GetLocation()!); return true; } + else if (this.Flags.Includes(BinderFlags.InFixedStatement) && + !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext) && + Compilation.IsFeatureEnabled(MessageID.IDS_FeatureUnsafeEvolution)) + { + Error(diagnostics, ErrorCode.ERR_BadAwaitInFixed, nodeOrToken.GetLocation()!); + return true; + } else if (this.Flags.Includes(BinderFlags.InLockBody)) { Error(diagnostics, ErrorCode.ERR_BadAwaitInLock, nodeOrToken.GetLocation()!); diff --git a/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs index 12504562e5a8f..36bcc6262cce2 100644 --- a/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs @@ -20,7 +20,7 @@ internal sealed class FixedStatementBinder : LocalScopeBinder private readonly FixedStatementSyntax _syntax; public FixedStatementBinder(Binder enclosing, FixedStatementSyntax syntax) - : base(enclosing) + : base(enclosing, enclosing.Flags | BinderFlags.InFixedStatement) { Debug.Assert(syntax != null); _syntax = syntax; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 9783e2213b05b..03c020d695ac9 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3834,6 +3834,9 @@ Give the compiler some way to differentiate the methods. For example, you can gi Cannot await in an unsafe context + + Cannot await in the body of a 'fixed' statement + The 'async' modifier can only be used in methods that have a body. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 16561f4fbb22e..02724981ad240 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2505,6 +2505,7 @@ internal enum ErrorCode ERR_NoBreakId = 9393, ERR_NoContinueId = 9394, ERR_ClosedBadDerivedTypesProperty = 9395, + ERR_BadAwaitInFixed = 9396, // Note: you will need to do the following after adding errors: // 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index f811408f2c8f0..2fe1d595c44e2 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -1551,6 +1551,7 @@ or ErrorCode.FTL_BadChecksumAlgorithm or ErrorCode.ERR_BadAwaitArgIntrinsic or ErrorCode.ERR_BadAwaitAsIdentifier or ErrorCode.ERR_AwaitInUnsafeContext + or ErrorCode.ERR_BadAwaitInFixed or ErrorCode.ERR_UnsafeAsyncArgType or ErrorCode.ERR_VarargsAsync or ErrorCode.ERR_BadAwaitArgVoidCall diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 2d10fed2d3c02..8ffd1bb81aa0e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -222,6 +222,11 @@ Argument {0} nelze předat s klíčovým slovem ref v jazykové verzi {1}. Pokud chcete argumenty ref předat parametrům in, upgradujte na verzi jazyka {2} nebo vyšší. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations Operátor {0} nejde použít pro operandy typu {1} a {2}, které nejsou bajtovým vyjádřením UTF-8. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 3be2016475e53..3ae8b630d0a2d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -222,6 +222,11 @@ Das Argument "{0}" darf nicht mit dem Schlüsselwort "ref" in der Sprachversion {1} übergeben werden. Um ref-Argumente an in-Parameter zu übergeben, führen Sie ein Upgrade auf die Sprachversion {2} oder höher durch. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations Der Operator ‚{0}‘ kann nicht auf Operanden des Typs ‚{1}‘ und ‚{2}‘ angewendet werden, die keine UTF-8-Bytedarstellungen sind diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 20e466e66bc9f..0d11d7e598bf5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -222,6 +222,11 @@ No se puede pasar el argumento {0} con la palabra clave "ref" en la versión de idioma {1}. Para pasar argumentos "ref" a parámetros "in", actualice a la versión de idioma {2} o superior. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations El operador '{0}' no se puede aplicar a operandos de tipo '{1}' y '{2}' que no sean representaciones de bytes UTF-8 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index bdc7bcb363ac7..f5e0cd82e3c5e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -222,6 +222,11 @@ L’argument {0} ne peut pas être passé avec le mot clé « ref » dans la version de langage {1}. Pour passer les arguments « ref » aux paramètres « in », effectuez une mise à niveau vers la version de langage {2} ou une version ultérieure. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations Impossible d’appliquer l’opérateur '{0}' aux opérandes de type '{1}' et '{2}' qui ne sont pas des représentations d’octets UTF-8 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8911634b7c293..66a4b10c48e40 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -222,6 +222,11 @@ Non è possibile passare l'argomento {0} con la parola chiave 'ref' nella versione del linguaggio {1}. Per passare gli argomenti 'ref' ai parametri 'in', eseguire l'aggiornamento alla versione del linguaggio {2} o versione successiva. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations Non è possibile applicare l'operatore '{0}' a operandi di tipo '{1}' e '{2}' che non sono rappresentazioni di UTF-8 byte diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 23cfc47581095..c4e1ba0ed73f5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -222,6 +222,11 @@ 言語バージョン {1} では、引数 {0} を 'ref' キーワードと共に渡すことはできません。'ref' 引数を 'in' パラメーターに渡すには、言語バージョン {2} 以上にアップグレードしてください。 + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations 演算子 '{0}' は、UTF-8 バイト表現ではない型 '{1}' および '{2}' のオペランドには適用できません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index afc0b3375fcc1..b98ee2b66b108 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -222,6 +222,11 @@ {0} 인수는 언어 버전 {1}의 'ref' 키워드와 함께 전달될 수 없습니다. 'ref' 인수를 'in' 매개 변수에 전달하려면 언어 버전 {2} 이상으로 업그레이드하세요. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations 연산자 '{0}'은(는) UTF-8 바이트 표현이 아닌 '{1}' 및 '{2}' 유형의 피연산자에 적용할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 72c956cfa9f4b..894116487bf13 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -222,6 +222,11 @@ Argument {0} nie może być przekazywany ze słowem kluczowym „ref” w wersji językowej {1}. Aby przekazać argumenty „ref” do parametrów „in”, uaktualnij do wersji językowej {2} lub nowszej. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations Nie można zastosować operatora „{0}” do argumentów operacji typu „{1}” i „{2}”, które nie są reprezentacjami bajtów UTF-8 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 72ed1b2ec83e8..7becfb4728d26 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -222,6 +222,11 @@ O argumento {0} não pode ser passado com o palavra-chave 'ref' na versão {1} da linguagem. Para passar argumentos 'ref' para parâmetros 'in', atualize para a versão {2} ou superior da linguagem. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations O operador '{0}' não pode ser aplicado a operandos do tipo '{1}' e '{2}' que não são representações de bytes UTF-8 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index f70a0ca4bb23c..89ef9ab14c518 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -222,6 +222,11 @@ Аргумент {0} не может передаваться с ключевым словом "ref" в языке версии {1}. Чтобы передать аргументы "ref" в параметры "in", повысьте статус до языка версии {2} или более поздней. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations Невозможно применить оператор "{0}" к операндам типа "{1}" и "{2}", которые не являются байтовыми представлениями UTF-8 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 8412c89a6f340..bd640e0cdcd1b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -222,6 +222,11 @@ {0} bağımsız değişkeni, {1} dil sürümündeki 'ref' anahtar sözcüğüyle geçirilemez. 'ref' bağımsız değişkenlerini 'in' parametrelerine geçirmek için{2} dil sürümüne yükseltin. + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations '{0}' işleci UTF-8 bayt gösterimleri olmayan '{1}' ve '{2}' işlenenlerine uygulanamaz diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index da7e2471d66b7..9e8fddc3faf3b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -222,6 +222,11 @@ 不能在语言版本 {1} 中使用 “ref” 关键字传递参数 {0}。要将 “ref” 参数传递给 “in” 参数,请升级到语言版本 {2} 或更高版本。 + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations 运算符 "{0}" 不能应用于类型为 "{1}" 和 "{2}" 的非 UTF-8 字节表示形式的操作数 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 737e1b9f40c60..fe3e1b226073a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -222,6 +222,11 @@ 引數 {0} 可能無法以語言版本 {1} 中的 'ref' 關鍵字傳遞。若要將 'ref' 引數傳遞至 'in' 參數,請升級為語言版本 {2} 或更新版本。 + + Cannot await in the body of a 'fixed' statement + Cannot await in the body of a 'fixed' statement + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' that are not UTF-8 byte representations 運算子 '{0}' 無法套用到型別為 '{1}' 的運算元,而 '{2}' 不是 UTF-8 位元組標記法 diff --git a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs index 394bd8a416c0e..029f034fb2c0c 100644 --- a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs +++ b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs @@ -4201,10 +4201,7 @@ async Task M() .VerifyDiagnostics( // (7,15): error CS9362: 'C.GetTask()' must be used in an unsafe context because it is marked as 'unsafe' // await GetTask(); - Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "GetTask()").WithArguments("C.GetTask()").WithLocation(7, 15), - // (9,20): error CS4004: Cannot await in an unsafe context - // _ = unsafe(await GetTask()); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await GetTask()").WithLocation(9, 20)); + Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "GetTask()").WithArguments("C.GetTask()").WithLocation(7, 15)); } [Fact] @@ -5069,7 +5066,7 @@ public void UnsafeContext_ExteriorOnly_AwaitAndYield() class C { - // Await in unsafe block is an error regardless of unsafe modifier. + // Await in an unsafe block is allowed under the unsafe evolution feature. async Task M1() { unsafe { await Task.Yield(); } @@ -5096,21 +5093,12 @@ unsafe IEnumerable M4() """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (9,18): error CS4004: Cannot await in an unsafe context - // unsafe { await Task.Yield(); } - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(9, 18), // (15,18): error CS9238: Cannot use 'yield return' in an 'unsafe' block // unsafe { yield return 1; } - Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(15, 18), - // (22,9): error CS4004: Cannot await in an unsafe context - // await Task.Yield(); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(22, 9)); + Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(15, 18)); // With updated rules: M3's body is NOT in unsafe context, so await is fine there. CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( - // (9,18): error CS4004: Cannot await in an unsafe context - // unsafe { await Task.Yield(); } - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(9, 18), // (15,18): error CS9238: Cannot use 'yield return' in an 'unsafe' block // unsafe { yield return 1; } Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(15, 18)); @@ -6720,10 +6708,7 @@ public void OnCompleted(Action continuation) { } """, ], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) - .VerifyDiagnostics( - // (1,10): error CS4004: Cannot await in an unsafe context - // unsafe { await new C(); } - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await new C()").WithLocation(1, 10)); + .VerifyDiagnostics(); } [Fact] @@ -6810,10 +6795,7 @@ public static async Task F() [corlib], parseOptions: WithRuntimeAsync(TestOptions.RegularPreview), options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) - .VerifyDiagnostics( - // (7,18): error CS4004: Cannot await in an unsafe context - // unsafe { await new Task(); } - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await new Task()").WithLocation(7, 18)); + .VerifyDiagnostics(); } [Fact] @@ -14878,4 +14860,199 @@ partial class C options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); } + + [Fact] + public void Await_InUnsafeBlock() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + unsafe + { + await Task.Yield(); + } + Console.Write(123); + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( + // (9,13): error CS4004: Cannot await in an unsafe context + // await Task.Yield(); + Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(9, 13)); + + var expectedOutput = "123"; + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Await_InFixedStatement_Body() + { + var source = """ + using System.Threading.Tasks; + class C + { + static async Task M(int[] a) + { + unsafe + { + fixed (int* p = a) + { + await Task.Yield(); + } + } + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (10,17): error CS4004: Cannot await in an unsafe context + // await Task.Yield(); + Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(10, 17)); + + var expectedDiagnostics = new[] + { + // (10,17): error CS9396: Cannot await in the body of a 'fixed' statement + // await Task.Yield(); + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await Task.Yield()").WithLocation(10, 17), + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Await_InFixedStatement_Body_SafeContext() + { + var source = """ + using System.Threading.Tasks; + class C + { + static async Task M(int[] a) + { + fixed (int* p = a) + { + await Task.Yield(); + } + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( + // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // fixed (int* p = a) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, @"fixed (int* p = a) + { + await Task.Yield(); + }").WithLocation(6, 9), + // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // fixed (int* p = a) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 16)); + + var expectedDiagnostics = new[] + { + // (8,13): error CS9396: Cannot await in the body of a 'fixed' statement + // await Task.Yield(); + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await Task.Yield()").WithLocation(8, 13), + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Await_InFixedStatement_Initializer() + { + var source = """ + using System.Threading.Tasks; + class C + { + static async Task M() + { + fixed (byte* p = await GetAsync()) + { + } + } + static Task GetAsync() => null; + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( + // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // fixed (byte* p = await GetAsync()) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, @"fixed (byte* p = await GetAsync()) + { + }").WithLocation(6, 9), + // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // fixed (byte* p = await GetAsync()) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16)); + + var expectedDiagnostics = new[] + { + // (6,26): error CS9396: Cannot await in the body of a 'fixed' statement + // fixed (byte* p = await GetAsync()) + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await GetAsync()").WithLocation(6, 26), + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Await_InFixedStatement_NestedLambda() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + static async Task M(int[] a) + { + fixed (int* p = a) + { + Func f = async () => { await Task.Yield(); }; + await f(); + } + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( + // (7,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // fixed (int* p = a) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, @"fixed (int* p = a) + { + Func f = async () => { await Task.Yield(); }; + await f(); + }").WithLocation(7, 9), + // (7,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // fixed (int* p = a) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 16)); + + var expectedDiagnostics = new[] + { + // (10,13): error CS9396: Cannot await in the body of a 'fixed' statement + // await f(); + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await f()").WithLocation(10, 13), + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs index cb16d88ed5a29..dccb45390ac81 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs @@ -2720,7 +2720,7 @@ public sealed class Enumerator public int* Current { get => throw null; } } }"; - var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.UnsafeDebugDll); + var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.Regular14); comp.VerifyDiagnostics( // (6,9): error CS4004: Cannot await in an unsafe context // await foreach (var i in new C()) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs index c998fc47ebde8..b09a4c417709a 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs @@ -287,7 +287,7 @@ public System.Threading.Tasks.ValueTask DisposeAsync() } } "; - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.UnsafeDebugDll); + var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.Regular14); comp.VerifyDiagnostics( // (8,13): error CS4004: Cannot await in an unsafe context // await using (var x = new C()) diff --git a/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs b/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs index 5f1377c7469c2..8b0d0704503a3 100644 --- a/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs @@ -264,7 +264,11 @@ async Task M(int x) }; CreateCompilation(source, parseOptions: TestOptions.Regular13, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(expectedDiagnostics); - CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics( + // (10,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(10, 34)); } [Fact] @@ -299,7 +303,11 @@ async Task M(int x) }; CreateCompilation(source, parseOptions: TestOptions.Regular13, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(expectedDiagnostics); - CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics( + // (8,30): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(8, 30)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs index 1a3b756dafc38..784d5274bbf44 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs @@ -743,20 +743,10 @@ class Test Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(9, 26) ); - var expectedPreviewDiagnostics = new[] - { - // (6,45): error CS4004: Cannot await in an unsafe context - // unsafe async static Task M1(int*[] i) { await Task.Yield(); } // 1 - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(6, 45), - // (7,56): error CS4004: Cannot await in an unsafe context - // unsafe async static Task M2(delegate*[] i) { await Task.Yield(); } // 2 - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(7, 56) - }; - CreateCompilationWithMscorlib461(source, null, TestOptions.UnsafeReleaseDll) - .VerifyDiagnostics(expectedPreviewDiagnostics); + .VerifyEmitDiagnostics(); CreateCompilationWithMscorlib461(source, null, TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext) - .VerifyDiagnostics(expectedPreviewDiagnostics); + .VerifyEmitDiagnostics(); } [Fact] @@ -1268,7 +1258,7 @@ async static Task M2() } } }"; - CreateCompilationWithMscorlib461(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + CreateCompilationWithMscorlib461(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular14).VerifyDiagnostics( // (7,9): error CS4004: Cannot await in an unsafe context // await Task.Factory.StartNew(() => { }); // not OK Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Factory.StartNew(() => { })"), @@ -1307,7 +1297,7 @@ static Task M2() } } }"; - CreateCompilationWithMscorlib461(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + CreateCompilationWithMscorlib461(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular14).VerifyDiagnostics( // (8,9): error CS4032: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'. // await Task.Factory.StartNew(() => { }); Diagnostic(ErrorCode.ERR_BadAwaitWithoutAsyncMethod, "await Task.Factory.StartNew(() => { })").WithArguments("System.Threading.Tasks.Task").WithLocation(8, 9), @@ -1559,7 +1549,8 @@ where l2 > 1 var c = CreateCompilationWithMscorlib461( source, new MetadataReference[] { SystemRef, LinqAssemblyRef }, - TestOptions.UnsafeReleaseDll); + TestOptions.UnsafeReleaseDll, + parseOptions: TestOptions.Regular14); c.VerifyDiagnostics( // (22,41): error CS4004: Cannot await in an unsafe context diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 6cae522b90c11..c9ef5ce022dad 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -4890,26 +4890,10 @@ async Task Local() // await Task.Delay(2); Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(2)").WithLocation(33, 13)); - var expectedPreviewDiagnostics = new[] - { - // (47,13): error CS4004: Cannot await in an unsafe context - // await Task.Delay(3); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(3)").WithLocation(47, 13), - // (22,17): error CS4004: Cannot await in an unsafe context - // await Task.Delay(1); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(1)").WithLocation(22, 17), - // (59,17): error CS4004: Cannot await in an unsafe context - // await Task.Delay(4); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(4)").WithLocation(59, 17), - // (33,13): error CS4004: Cannot await in an unsafe context - // await Task.Delay(2); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(2)").WithLocation(33, 13) - }; - CreateCompilationWithMscorlib46(source, options: TestOptions.UnsafeDebugDll) - .VerifyDiagnostics(expectedPreviewDiagnostics); + .VerifyEmitDiagnostics(); CreateCompilationWithMscorlib46(source, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularNext) - .VerifyDiagnostics(expectedPreviewDiagnostics); + .VerifyEmitDiagnostics(); } [Fact, WorkItem(16167, "https://github.com/dotnet/roslyn/issues/16167")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs index 85e4cbe790b83..b0f2397882bf8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs @@ -729,7 +729,7 @@ async void M() { var p = stackalloc int[await Task.FromResult(1)] { await Task.FromResult(2) }; } -}", TestOptions.UnsafeReleaseDll); +}", TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular14); comp.VerifyDiagnostics( // (7,32): error CS4004: Cannot await in an unsafe context // var p = stackalloc int[await Task.FromResult(1)] { await Task.FromResult(2) }; From 07521503f5b3ed2d2c98e561e3ee6c0d259fbaa9 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 24 Jul 2026 13:56:21 +0200 Subject: [PATCH 2/9] Test BinderFlags.AllowAwaitInUnsafeContext --- .../ExpressionCompilerTests.cs | 61 +++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index def8e433365d1..689256ab5b2f7 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -2391,10 +2391,11 @@ static void Main() } /// - /// This would be illegal in any non-debugger context. + /// This would be illegal in any non-debugger context before the unsafe evolution feature. /// - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1075258")] - public void AwaitInUnsafeContext() + [Theory, CombinatorialData, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1075258")] + public void AwaitInUnsafeContext( + [CombinatorialValues(LanguageVersion.CSharp14, LanguageVersionFacts.CSharpNext, LanguageVersion.Preview)] LanguageVersion langVersion) { var source = @" using System; @@ -2414,7 +2415,9 @@ static void Main() } } "; - var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.UnsafeDebugDll); + var comp = CreateCompilationWithMscorlib461(source, + parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), + options: TestOptions.UnsafeDebugDll); WithRuntimeInstance(comp, runtime => { var context = CreateMethodContext(runtime, "C.Main"); @@ -2431,6 +2434,56 @@ static void Main() }); } + /// + /// 'await' inside a 'fixed' statement is illegal in any non-debugger context, + /// but the debugger sets , which + /// also lifts the 'fixed' restriction. + /// + [Theory, CombinatorialData] + public void AwaitInFixedStatement( + [CombinatorialValues(LanguageVersion.CSharp14, LanguageVersionFacts.CSharpNext, LanguageVersion.Preview)] LanguageVersion langVersion) + { + var source = @" +using System; +using System.Threading.Tasks; + +class C +{ + static async Task F() + { + return null; + } + static void G(Func> f) + { + } + static void Main() + { + } +} +"; + var comp = CreateCompilationWithMscorlib461(source, + parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), + options: TestOptions.UnsafeDebugDll); + WithRuntimeInstance(comp, runtime => + { + var context = CreateMethodContext(runtime, "C.Main"); + string error; + var testData = new CompilationTestData(); + context.CompileExpression(@"G(async() => +{ + int[] a = new int[1]; + unsafe + { + fixed (int* p = a) + { + return await F(); + } + } +})", out error, testData); + Assert.Null(error); + }); + } + /// /// Flow analysis should catch definite assignment errors /// for variables declared within the expression. From abd75d75c0c092a668a2d97f1157928b35367d7d Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 30 Jul 2026 12:59:28 +0200 Subject: [PATCH 3/9] Improve diagnostics --- .../CSharp/Portable/Binder/BinderFlags.cs | 4 +- .../CSharp/Portable/Binder/Binder_Await.cs | 20 ++++----- .../CSharp/Portable/Binder/Binder_Symbols.cs | 2 + .../Portable/Binder/FixedStatementBinder.cs | 2 +- .../Portable/Binder/LocalBinderFactory.cs | 4 +- .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../CSharp/Portable/Errors/ErrorFacts.cs | 1 - .../Test/CSharp15/UnsafeEvolutionTests.cs | 41 ++++++++----------- .../Emit/CodeGen/CodeGenAwaitForeachTests.cs | 4 +- .../Emit/CodeGen/CodeGenAwaitUsingTests.cs | 4 +- .../Emit3/RefUnsafeInIteratorAndAsyncTests.cs | 16 ++++---- .../Semantic/Semantics/BindingAsyncTests.cs | 24 +++++------ .../Semantic/Semantics/LocalFunctionTests.cs | 16 ++++---- .../Semantics/StackAllocInitializerTests.cs | 8 ++-- .../Test/Semantic/Semantics/UnsafeTests.cs | 12 +++--- 15 files changed, 78 insertions(+), 82 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs index f54662cdf0a80..1f808cdbcc8ee 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs @@ -19,7 +19,7 @@ internal enum BinderFlags : uint SuppressObsoleteChecks = 1 << 1, ConstructorInitializer = 1 << 2, FieldInitializer = 1 << 3, - InFixedStatement = 1 << 4, // body or declaration of a 'fixed' statement (used to disallow 'await') + InFixedStatementBody = 1 << 4, // body of a 'fixed' statement (used to disallow 'await') CollectionInitializerAddMethod = 1 << 5, // used for collection initializer add method overload resolution diagnostics AttributeArgument = 1 << 6, GenericConstraintsClause = 1 << 7, // "where" clause (used for cycle checking) @@ -120,6 +120,6 @@ internal enum BinderFlags : uint // Groups - AllClearedAtExecutableCodeBoundary = InLockBody | InCatchBlock | InCatchFilter | InFinallyBlock | InTryBlockOfTryCatch | InNestedFinallyBlock | InFixedStatement, + AllClearedAtExecutableCodeBoundary = InLockBody | InCatchBlock | InCatchFilter | InFinallyBlock | InTryBlockOfTryCatch | InNestedFinallyBlock | InFixedStatementBody, } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index 704044b853e91..b573b9d434c5d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -218,18 +218,12 @@ private bool ReportBadAwaitWithoutAsync(SyntaxNodeOrToken nodeOrToken, BindingDi /// True if errors were found. private bool ReportBadAwaitContext(SyntaxNodeOrToken nodeOrToken, BindingDiagnosticBag diagnostics) { - if (this.InUnsafeRegion && - !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext) && - !Compilation.IsFeatureEnabled(MessageID.IDS_FeatureUnsafeEvolution)) - { - Error(diagnostics, ErrorCode.ERR_AwaitInUnsafeContext, nodeOrToken.GetLocation()!); - return true; - } - else if (this.Flags.Includes(BinderFlags.InFixedStatement) && - !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext) && - Compilation.IsFeatureEnabled(MessageID.IDS_FeatureUnsafeEvolution)) + // Better error location than `nodeOrToken` but using it only for new diagnostics to avoid test churn. + var awaitNodeOrToken = nodeOrToken.AsNode() is AwaitExpressionSyntax awaitExpression ? awaitExpression.AwaitKeyword : nodeOrToken; + + if (this.Flags.Includes(BinderFlags.InFixedStatementBody)) { - Error(diagnostics, ErrorCode.ERR_BadAwaitInFixed, nodeOrToken.GetLocation()!); + Error(diagnostics, ErrorCode.ERR_BadAwaitInFixed, awaitNodeOrToken); return true; } else if (this.Flags.Includes(BinderFlags.InLockBody)) @@ -242,6 +236,10 @@ private bool ReportBadAwaitContext(SyntaxNodeOrToken nodeOrToken, BindingDiagnos Error(diagnostics, ErrorCode.ERR_BadAwaitInCatchFilter, nodeOrToken.GetLocation()!); return true; } + else if (this.InUnsafeRegion && !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext)) + { + return !CheckFeatureAvailability(awaitNodeOrToken, MessageID.IDS_FeatureUnsafeEvolution, diagnostics); + } else if (this.Flags.Includes(BinderFlags.InFinallyBlock) && (nodeOrToken.SyntaxTree as CSharpSyntaxTree)?.Options?.IsFeatureEnabled(MessageID.IDS_AwaitInCatchAndFinally) == false) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 09f8a710183b1..b3a685c6d4b77 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -2819,6 +2819,8 @@ protected AssemblySymbol GetForwardedToAssembly(string name, int arity, ref Name } #nullable enable + internal static bool CheckFeatureAvailability(SyntaxNodeOrToken syntax, MessageID feature, BindingDiagnosticBag diagnostics, Location? location = null) + => CheckFeatureAvailability(syntax.SyntaxTree!, feature, diagnostics.DiagnosticBag, (location, syntax), static tuple => tuple.location ?? tuple.syntax.GetLocation()!); internal static bool CheckFeatureAvailability(SyntaxNode syntax, MessageID feature, BindingDiagnosticBag diagnostics, Location? location = null) => CheckFeatureAvailability(syntax, feature, diagnostics.DiagnosticBag, location); diff --git a/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs index 36bcc6262cce2..12504562e5a8f 100644 --- a/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs @@ -20,7 +20,7 @@ internal sealed class FixedStatementBinder : LocalScopeBinder private readonly FixedStatementSyntax _syntax; public FixedStatementBinder(Binder enclosing, FixedStatementSyntax syntax) - : base(enclosing, enclosing.Flags | BinderFlags.InFixedStatement) + : base(enclosing) { Debug.Assert(syntax != null); _syntax = syntax; diff --git a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs index e37a611b00dbb..1c2e8d6b17577 100644 --- a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs @@ -720,7 +720,9 @@ public override void VisitFixedStatement(FixedStatementSyntax node) } } - VisitPossibleEmbeddedStatement(node.Statement, binder); + var bodyBinder = binder.WithAdditionalFlags(BinderFlags.InFixedStatementBody); + AddToMap(node.Statement, bodyBinder); + VisitPossibleEmbeddedStatement(node.Statement, bodyBinder); } public override void VisitLockStatement(LockStatementSyntax node) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 02724981ad240..6314097cc7274 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1103,7 +1103,7 @@ internal enum ErrorCode ERR_BadAwaitArgIntrinsic = 4001, // 4002 unused ERR_BadAwaitAsIdentifier = 4003, - ERR_AwaitInUnsafeContext = 4004, + // ERR_AwaitInUnsafeContext = 4004, // replaced with a langversion error ERR_UnsafeAsyncArgType = 4005, ERR_VarargsAsync = 4006, ERR_ByRefTypeAndAwait = 4007, diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 2fe1d595c44e2..c17fae46c5988 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -1550,7 +1550,6 @@ or ErrorCode.WRN_CLS_BadInterface or ErrorCode.FTL_BadChecksumAlgorithm or ErrorCode.ERR_BadAwaitArgIntrinsic or ErrorCode.ERR_BadAwaitAsIdentifier - or ErrorCode.ERR_AwaitInUnsafeContext or ErrorCode.ERR_BadAwaitInFixed or ErrorCode.ERR_UnsafeAsyncArgType or ErrorCode.ERR_VarargsAsync diff --git a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs index 029f034fb2c0c..0bf2ba26cee0a 100644 --- a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs +++ b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs @@ -14881,9 +14881,9 @@ static async Task Main() """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( - // (9,13): error CS4004: Cannot await in an unsafe context + // (9,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Yield(); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(9, 13)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(9, 13)); var expectedOutput = "123"; @@ -14913,18 +14913,14 @@ static async Task M(int[] a) } """; - CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (10,17): error CS4004: Cannot await in an unsafe context - // await Task.Yield(); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(10, 17)); - var expectedDiagnostics = new[] { // (10,17): error CS9396: Cannot await in the body of a 'fixed' statement // await Task.Yield(); - Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await Task.Yield()").WithLocation(10, 17), + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 17), }; + CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); @@ -14957,13 +14953,16 @@ static async Task M(int[] a) }").WithLocation(6, 9), // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) - Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 16)); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 16), + // (8,13): error CS9396: Cannot await in the body of a 'fixed' statement + // await Task.Yield(); + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13)); var expectedDiagnostics = new[] { // (8,13): error CS9396: Cannot await in the body of a 'fixed' statement // await Task.Yield(); - Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await Task.Yield()").WithLocation(8, 13), + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13), }; CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); @@ -14999,17 +14998,10 @@ static async Task M() // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16)); - var expectedDiagnostics = new[] - { - // (6,26): error CS9396: Cannot await in the body of a 'fixed' statement - // fixed (byte* p = await GetAsync()) - Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await GetAsync()").WithLocation(6, 26), - }; - - CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); - CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); - CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); - CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(); } [Fact] @@ -15041,13 +15033,16 @@ static async Task M(int[] a) }").WithLocation(7, 9), // (7,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) - Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 16)); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 16), + // (10,13): error CS9396: Cannot await in the body of a 'fixed' statement + // await f(); + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13)); var expectedDiagnostics = new[] { // (10,13): error CS9396: Cannot await in the body of a 'fixed' statement // await f(); - Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await f()").WithLocation(10, 13), + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13), }; CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs index dccb45390ac81..fd0ac3fe7de32 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs @@ -2722,9 +2722,9 @@ public sealed class Enumerator }"; var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.Regular14); comp.VerifyDiagnostics( - // (6,9): error CS4004: Cannot await in an unsafe context + // (6,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await foreach (var i in new C()) - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await").WithLocation(6, 9)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(6, 9)); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs index b09a4c417709a..b9afbd8500be1 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs @@ -289,9 +289,9 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.Regular14); comp.VerifyDiagnostics( - // (8,13): error CS4004: Cannot await in an unsafe context + // (8,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await using (var x = new C()) - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await").WithLocation(8, 13) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(8, 13) ); } diff --git a/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs b/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs index 8b0d0704503a3..0ad0870eaeeed 100644 --- a/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs @@ -252,15 +252,15 @@ async Task M(int x) // (8,21): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // ref int y = ref x; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "y").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(8, 21), - // (9,13): error CS4004: Cannot await in an unsafe context + // (9,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Yield(); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(9, 13)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(9, 13)); var expectedDiagnostics = new[] { - // (9,13): error CS4004: Cannot await in an unsafe context + // (9,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Yield(); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(9, 13) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(9, 13) }; CreateCompilation(source, parseOptions: TestOptions.Regular13, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(expectedDiagnostics); @@ -291,15 +291,15 @@ async Task M(int x) // (6,17): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // ref int y = ref x; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "y").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 17), - // (7,9): error CS4004: Cannot await in an unsafe context + // (7,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Yield(); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(7, 9)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(7, 9)); var expectedDiagnostics = new[] { - // (7,9): error CS4004: Cannot await in an unsafe context + // (7,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Yield(); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(7, 9) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(7, 9) }; CreateCompilation(source, parseOptions: TestOptions.Regular13, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(expectedDiagnostics); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs index 784d5274bbf44..099d4db5384ff 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs @@ -729,12 +729,12 @@ class Test }"; CreateCompilationWithMscorlib461(source, null, TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular14) .VerifyDiagnostics( - // (6,45): error CS4004: Cannot await in an unsafe context + // (6,45): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // unsafe async static Task M1(int*[] i) { await Task.Yield(); } // 1 - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(6, 45), - // (7,56): error CS4004: Cannot await in an unsafe context + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(6, 45), + // (7,56): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // unsafe async static Task M2(delegate*[] i) { await Task.Yield(); } // 2 - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(7, 56), + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(7, 56), // (8,26): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // async static Task M3(int*[] i) { await Task.Yield(); } // 3 Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(8, 26), @@ -1259,12 +1259,12 @@ async static Task M2() } }"; CreateCompilationWithMscorlib461(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular14).VerifyDiagnostics( - // (7,9): error CS4004: Cannot await in an unsafe context + // (7,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Factory.StartNew(() => { }); // not OK - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Factory.StartNew(() => { })"), - // (16,13): error CS4004: Cannot await in an unsafe context + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(7, 9), + // (16,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Factory.StartNew(() => { }); // not OK - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Factory.StartNew(() => { })")); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(16, 13)); } [Fact] @@ -1304,9 +1304,9 @@ static Task M2() // (12,13): error CS4032: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'. // await Task.Factory.StartNew(() => { }); Diagnostic(ErrorCode.ERR_BadAwaitWithoutAsyncMethod, "await Task.Factory.StartNew(() => { })").WithArguments("System.Threading.Tasks.Task").WithLocation(12, 13), - // (12,13): error CS4004: Cannot await in an unsafe context + // (12,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Factory.StartNew(() => { }); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Factory.StartNew(() => { })").WithLocation(12, 13), + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(12, 13), // (20,13): error CS4032: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'. // await Task.Factory.StartNew(() => { }); Diagnostic(ErrorCode.ERR_BadAwaitWithoutAsyncMethod, "await Task.Factory.StartNew(() => { })").WithArguments("System.Threading.Tasks.Task").WithLocation(20, 13), @@ -1553,9 +1553,9 @@ where l2 > 1 parseOptions: TestOptions.Regular14); c.VerifyDiagnostics( - // (22,41): error CS4004: Cannot await in an unsafe context + // (22,41): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // join l3 in await F1() on l2 equals l3 - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await F1()"), + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(22, 41), // (22,41): error CS1995: The 'await' operator may only be used in a query expression within the first collection expression of the initial 'from' clause or within the collection expression of a 'join' clause // join l3 in await F1() on l2 equals l3 Diagnostic(ErrorCode.ERR_BadAwaitInQuery, "await F1()")); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index c9ef5ce022dad..82548794be5b7 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -4877,18 +4877,18 @@ async Task Local() // (11,28): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // return (IntPtr)(void*)null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "(void*)null").WithLocation(11, 28), - // (47,13): error CS4004: Cannot await in an unsafe context + // (47,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Delay(3); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(3)").WithLocation(47, 13), - // (22,17): error CS4004: Cannot await in an unsafe context + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(47, 13), + // (22,17): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Delay(1); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(1)").WithLocation(22, 17), - // (59,17): error CS4004: Cannot await in an unsafe context + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(22, 17), + // (59,17): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Delay(4); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(4)").WithLocation(59, 17), - // (33,13): error CS4004: Cannot await in an unsafe context + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(59, 17), + // (33,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Delay(2); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(2)").WithLocation(33, 13)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(33, 13)); CreateCompilationWithMscorlib46(source, options: TestOptions.UnsafeDebugDll) .VerifyEmitDiagnostics(); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs index b0f2397882bf8..35fc4b84f417e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs @@ -731,12 +731,12 @@ async void M() } }", TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular14); comp.VerifyDiagnostics( - // (7,32): error CS4004: Cannot await in an unsafe context + // (7,32): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var p = stackalloc int[await Task.FromResult(1)] { await Task.FromResult(2) }; - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.FromResult(1)").WithLocation(7, 32), - // (7,60): error CS4004: Cannot await in an unsafe context + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(7, 32), + // (7,60): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var p = stackalloc int[await Task.FromResult(1)] { await Task.FromResult(2) }; - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.FromResult(2)").WithLocation(7, 60) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(7, 60) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 4ee4c0022fd4f..36fd6d651dd7b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -870,11 +870,11 @@ IEnumerable M() } """; - // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors + // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors for upgrading to C#13 CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( - // (8,31): error CS4004: Cannot await in an unsafe context + // (8,31): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var lam = async () => await Task.Yield(); - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(8, 31)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(8, 31)); CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(); CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); @@ -897,11 +897,11 @@ IEnumerable M() } """; - // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors + // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors for upgrading to C#13 CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( - // (9,30): error CS4004: Cannot await in an unsafe context + // (9,30): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // async void local() { await Task.Yield(); } - Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(9, 30)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(9, 30)); CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics(); CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); From e1230d15a8da4af62dcf89266dd335e78472416a Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 30 Jul 2026 14:27:50 +0200 Subject: [PATCH 4/9] Fixup EE --- src/Compilers/CSharp/Portable/Binder/Binder_Await.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index b573b9d434c5d..1264db6f8549a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -221,7 +221,7 @@ private bool ReportBadAwaitContext(SyntaxNodeOrToken nodeOrToken, BindingDiagnos // Better error location than `nodeOrToken` but using it only for new diagnostics to avoid test churn. var awaitNodeOrToken = nodeOrToken.AsNode() is AwaitExpressionSyntax awaitExpression ? awaitExpression.AwaitKeyword : nodeOrToken; - if (this.Flags.Includes(BinderFlags.InFixedStatementBody)) + if (this.Flags.Includes(BinderFlags.InFixedStatementBody) && !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext)) { Error(diagnostics, ErrorCode.ERR_BadAwaitInFixed, awaitNodeOrToken); return true; From df329a54b8c43d6aaafeea7ed942e7d7da399f69 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 30 Jul 2026 17:14:53 +0200 Subject: [PATCH 5/9] Report for initializer again --- .../CSharp/Portable/Binder/BinderFlags.cs | 4 +- .../CSharp/Portable/Binder/Binder_Await.cs | 2 +- .../Portable/Binder/FixedStatementBinder.cs | 2 +- .../Portable/Binder/LocalBinderFactory.cs | 4 +- .../CSharp/Portable/CSharpResources.resx | 5 +- .../Portable/xlf/CSharpResources.cs.xlf | 9 +-- .../Portable/xlf/CSharpResources.de.xlf | 9 +-- .../Portable/xlf/CSharpResources.es.xlf | 9 +-- .../Portable/xlf/CSharpResources.fr.xlf | 9 +-- .../Portable/xlf/CSharpResources.it.xlf | 9 +-- .../Portable/xlf/CSharpResources.ja.xlf | 9 +-- .../Portable/xlf/CSharpResources.ko.xlf | 9 +-- .../Portable/xlf/CSharpResources.pl.xlf | 9 +-- .../Portable/xlf/CSharpResources.pt-BR.xlf | 9 +-- .../Portable/xlf/CSharpResources.ru.xlf | 9 +-- .../Portable/xlf/CSharpResources.tr.xlf | 9 +-- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 9 +-- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 9 +-- .../Test/CSharp15/UnsafeEvolutionTests.cs | 79 ++++++++++++++++--- 19 files changed, 101 insertions(+), 112 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs index 1f808cdbcc8ee..b3ee9b2633eaa 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs @@ -19,7 +19,7 @@ internal enum BinderFlags : uint SuppressObsoleteChecks = 1 << 1, ConstructorInitializer = 1 << 2, FieldInitializer = 1 << 3, - InFixedStatementBody = 1 << 4, // body of a 'fixed' statement (used to disallow 'await') + InFixedStatement = 1 << 4, // body or initializer of a 'fixed' statement (used to disallow 'await') CollectionInitializerAddMethod = 1 << 5, // used for collection initializer add method overload resolution diagnostics AttributeArgument = 1 << 6, GenericConstraintsClause = 1 << 7, // "where" clause (used for cycle checking) @@ -120,6 +120,6 @@ internal enum BinderFlags : uint // Groups - AllClearedAtExecutableCodeBoundary = InLockBody | InCatchBlock | InCatchFilter | InFinallyBlock | InTryBlockOfTryCatch | InNestedFinallyBlock | InFixedStatementBody, + AllClearedAtExecutableCodeBoundary = InLockBody | InCatchBlock | InCatchFilter | InFinallyBlock | InTryBlockOfTryCatch | InNestedFinallyBlock | InFixedStatement, } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index 1264db6f8549a..70f23e6dccfb7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -221,7 +221,7 @@ private bool ReportBadAwaitContext(SyntaxNodeOrToken nodeOrToken, BindingDiagnos // Better error location than `nodeOrToken` but using it only for new diagnostics to avoid test churn. var awaitNodeOrToken = nodeOrToken.AsNode() is AwaitExpressionSyntax awaitExpression ? awaitExpression.AwaitKeyword : nodeOrToken; - if (this.Flags.Includes(BinderFlags.InFixedStatementBody) && !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext)) + if (this.Flags.Includes(BinderFlags.InFixedStatement) && !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext)) { Error(diagnostics, ErrorCode.ERR_BadAwaitInFixed, awaitNodeOrToken); return true; diff --git a/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs index 12504562e5a8f..36bcc6262cce2 100644 --- a/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs @@ -20,7 +20,7 @@ internal sealed class FixedStatementBinder : LocalScopeBinder private readonly FixedStatementSyntax _syntax; public FixedStatementBinder(Binder enclosing, FixedStatementSyntax syntax) - : base(enclosing) + : base(enclosing, enclosing.Flags | BinderFlags.InFixedStatement) { Debug.Assert(syntax != null); _syntax = syntax; diff --git a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs index 1c2e8d6b17577..e37a611b00dbb 100644 --- a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs @@ -720,9 +720,7 @@ public override void VisitFixedStatement(FixedStatementSyntax node) } } - var bodyBinder = binder.WithAdditionalFlags(BinderFlags.InFixedStatementBody); - AddToMap(node.Statement, bodyBinder); - VisitPossibleEmbeddedStatement(node.Statement, bodyBinder); + VisitPossibleEmbeddedStatement(node.Statement, binder); } public override void VisitLockStatement(LockStatementSyntax node) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 03c020d695ac9..4e6550fa42a0d 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3831,11 +3831,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi The 'await' operator cannot be used in a static script variable initializer. - - Cannot await in an unsafe context - - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement The 'async' modifier can only be used in methods that have a body. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 8ffd1bb81aa0e..fc7fe133e9d7a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např Operátor await nejde použít v inicializátoru proměnné statického skriptu. - - Cannot await in an unsafe context - Operátor await nejde použít v nezabezpečeném kontextu. - - The 'async' modifier can only be used in methods that have a body. Modifikátor async se dá použít jenom v metodách, které mají tělo. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 3ae8b630d0a2d..0795ecfbb5367 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz Der Operator "await" kann nicht in einem statischen Skriptvariableninitialisierer verwendet werden. - - Cannot await in an unsafe context - Kann nicht in unsicherem Kontext warten. - - The 'async' modifier can only be used in methods that have a body. Der Modifizierer "async" kann nur in Methoden verwendet werden, die über einen Textkörper verfügen. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 0d11d7e598bf5..5fe01e33fb423 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue El operador 'await' no se puede usar en un inicializador de variable de script estático. - - Cannot await in an unsafe context - No se puede usar await en un contexto no seguro. - - The 'async' modifier can only be used in methods that have a body. El modificador 'async' solo se puede usar en métodos que tengan un cuerpo. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index f5e0cd82e3c5e..d6af9f33777f3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve L'opérateur 'await' ne peut pas être utilisé dans un initialiseur de variable de script statique. - - Cannot await in an unsafe context - Impossible d'attendre dans un contexte unsafe - - The 'async' modifier can only be used in methods that have a body. Le modificateur 'async' ne peut être utilisé que dans des méthodes ayant un corps. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 66a4b10c48e40..6a1af37e52c31 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn Non è possibile usare l'operatore 'await' in un inizializzatore di variabile script statico. - - Cannot await in an unsafe context - Non è possibile attendere in un contesto non sicuro - - The 'async' modifier can only be used in methods that have a body. Il modificatore 'async' può essere usato solo nei metodi con un corpo. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index c4e1ba0ed73f5..8955dfe8718fc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ C# では out と ref を区別しますが、CLR では同じと認識します await' 演算子は、静的なスクリプト変数初期化子では使用できません。 - - Cannot await in an unsafe context - unsafe コンテキストで待機することはできません - - The 'async' modifier can only be used in methods that have a body. async' 修飾子は、本体があるメソッドでのみ使用できます。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index b98ee2b66b108..ae97d206dc5c6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간 await' 연산자는 정적 스크립트 변수 이니셜라이저에서 사용할 수 없습니다. - - Cannot await in an unsafe context - 안전하지 않은 컨텍스트에서는 await를 사용할 수 없습니다. - - The 'async' modifier can only be used in methods that have a body. async' 한정자는 본문이 있는 메서드에서만 사용할 수 있습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 894116487bf13..e3f08e526bb9b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada Operatora „await” nie można użyć w inicjalizatorze statycznej zmiennej skryptu. - - Cannot await in an unsafe context - Nie można zdefiniować oczekiwania w kontekście słowa kluczowego „unsafe”. - - The 'async' modifier can only be used in methods that have a body. Modyfikatora „async” można używać tylko w metodach mających treść. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 7becfb4728d26..72b59ee64e90c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc O operador 'await' não pode ser usado em um inicializador de variável de script estático. - - Cannot await in an unsafe context - Não é possível esperar em um contexto sem segurança - - The 'async' modifier can only be used in methods that have a body. O modificador 'async' só pode ser usado em métodos que têm um corpo. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 89ef9ab14c518..4cd9202f4b2dd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi Оператор "await" невозможно использовать в инициализаторе статической переменной скрипта. - - Cannot await in an unsafe context - Невозможно ожидание в небезопасном контексте. - - The 'async' modifier can only be used in methods that have a body. Модификатор "async" можно использовать только в методах, имеющих тело. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index bd640e0cdcd1b..d3f4b640cce39 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl Await' işleci bir statik betik değişken başlatıcısında kullanılamaz. - - Cannot await in an unsafe context - Güvenli olmayan bir bağlamda await kullanılamaz - - The 'async' modifier can only be used in methods that have a body. Async' değiştiricisi yalnızca gövdesi olan metotlarda kullanılabilir. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 9e8fddc3faf3b..dcf72846fe4c5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi 静态脚本变量初始值设定项中不可使用 "await" 运算符。 - - Cannot await in an unsafe context - 无法在不安全的上下文中等待 - - The 'async' modifier can only be used in methods that have a body. 只能在具有正文的方法中使用 "async" 修饰符。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index fe3e1b226073a..984d198f5647c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -223,8 +223,8 @@ - Cannot await in the body of a 'fixed' statement - Cannot await in the body of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in the body or the initializer of a 'fixed' statement @@ -11441,11 +11441,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi await' 運算子不可用於靜態指令碼變數初始設定式。 - - Cannot await in an unsafe context - 無法在不安全的內容中等候 - - The 'async' modifier can only be used in methods that have a body. async' 修飾元只可用於具有主體的方法。 diff --git a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs index 0bf2ba26cee0a..f1168bd86b221 100644 --- a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs +++ b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs @@ -14915,7 +14915,7 @@ static async Task M(int[] a) var expectedDiagnostics = new[] { - // (10,17): error CS9396: Cannot await in the body of a 'fixed' statement + // (10,17): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 17), }; @@ -14954,13 +14954,13 @@ static async Task M(int[] a) // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 16), - // (8,13): error CS9396: Cannot await in the body of a 'fixed' statement + // (8,13): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13)); var expectedDiagnostics = new[] { - // (8,13): error CS9396: Cannot await in the body of a 'fixed' statement + // (8,13): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13), }; @@ -14996,12 +14996,71 @@ static async Task M() }").WithLocation(6, 9), // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (byte* p = await GetAsync()) - Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16)); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16), + // (6,26): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // fixed (byte* p = await GetAsync()) + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 26)); + + var expectedDiagnostics = new[] + { + // (6,26): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // fixed (byte* p = await GetAsync()) + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 26), + }; - CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); - CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(); - CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); - CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Await_InFixedStatement_Initializer_Multiple() + { + var source = """ + using System.Threading.Tasks; + class C + { + static async Task M() + { + fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) + { + } + } + static Task GetAsync() => null; + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( + // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, @"fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) + { + }").WithLocation(6, 9), + // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16), + // (6,27): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 27), + // (6,50): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 50)); + + var expectedDiagnostics = new[] + { + // (6,27): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 27), + // (6,50): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) + Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 50), + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -15034,13 +15093,13 @@ static async Task M(int[] a) // (7,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 16), - // (10,13): error CS9396: Cannot await in the body of a 'fixed' statement + // (10,13): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement // await f(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13)); var expectedDiagnostics = new[] { - // (10,13): error CS9396: Cannot await in the body of a 'fixed' statement + // (10,13): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement // await f(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13), }; From 839be1d6cf8b40cbbc13001f75d3ad032b38ece0 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 30 Jul 2026 17:19:57 +0200 Subject: [PATCH 6/9] Clarify --- src/Compilers/CSharp/Portable/Binder/Binder_Await.cs | 5 +++-- src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index 70f23e6dccfb7..144832d77dd22 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -236,9 +236,10 @@ private bool ReportBadAwaitContext(SyntaxNodeOrToken nodeOrToken, BindingDiagnos Error(diagnostics, ErrorCode.ERR_BadAwaitInCatchFilter, nodeOrToken.GetLocation()!); return true; } - else if (this.InUnsafeRegion && !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext)) + else if (this.InUnsafeRegion && !this.Flags.Includes(BinderFlags.AllowAwaitInUnsafeContext) && + !CheckFeatureAvailability(awaitNodeOrToken, MessageID.IDS_FeatureUnsafeEvolution, diagnostics)) { - return !CheckFeatureAvailability(awaitNodeOrToken, MessageID.IDS_FeatureUnsafeEvolution, diagnostics); + return true; } else if (this.Flags.Includes(BinderFlags.InFinallyBlock) && (nodeOrToken.SyntaxTree as CSharpSyntaxTree)?.Options?.IsFeatureEnabled(MessageID.IDS_AwaitInCatchAndFinally) == false) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 36fd6d651dd7b..a0698a5cc8a38 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -870,7 +870,7 @@ IEnumerable M() } """; - // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors for upgrading to C#13 + // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors for 'ref and unsafe in async and iterator methods' CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( // (8,31): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var lam = async () => await Task.Yield(); @@ -897,7 +897,7 @@ IEnumerable M() } """; - // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors for upgrading to C#13 + // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors for 'ref and unsafe in async and iterator methods' CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( // (9,30): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // async void local() { await Task.Yield(); } From 8e3dd126ecaf63bffd6164a006693e8cb15ff09d Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 30 Jul 2026 18:09:29 +0200 Subject: [PATCH 7/9] Simplify error message --- .../CSharp/Portable/CSharpResources.resx | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 ++-- .../Portable/xlf/CSharpResources.de.xlf | 4 ++-- .../Portable/xlf/CSharpResources.es.xlf | 4 ++-- .../Portable/xlf/CSharpResources.fr.xlf | 4 ++-- .../Portable/xlf/CSharpResources.it.xlf | 4 ++-- .../Portable/xlf/CSharpResources.ja.xlf | 4 ++-- .../Portable/xlf/CSharpResources.ko.xlf | 4 ++-- .../Portable/xlf/CSharpResources.pl.xlf | 4 ++-- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 ++-- .../Portable/xlf/CSharpResources.ru.xlf | 4 ++-- .../Portable/xlf/CSharpResources.tr.xlf | 4 ++-- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 ++-- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 ++-- .../Test/CSharp15/UnsafeEvolutionTests.cs | 22 +++++++++---------- 15 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 4e6550fa42a0d..591d1b4e1e162 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3832,7 +3832,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi The 'await' operator cannot be used in a static script variable initializer. - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement The 'async' modifier can only be used in methods that have a body. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index fc7fe133e9d7a..c7dd3d9ee3caa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 0795ecfbb5367..f120f94f39165 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 5fe01e33fb423..23cab0ef478e9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index d6af9f33777f3..586b17517215e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 6a1af37e52c31..ce0ceea04000f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 8955dfe8718fc..04d6f58e56306 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index ae97d206dc5c6..a0fc6517b966e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index e3f08e526bb9b..f9b6f1cff22cc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 72b59ee64e90c..857f84902abf6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 4cd9202f4b2dd..43416bd4be36e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index d3f4b640cce39..005837548816f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index dcf72846fe4c5..05ac800e4b363 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 984d198f5647c..d02fa5163f8da 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -223,8 +223,8 @@ - Cannot await in the body or the initializer of a 'fixed' statement - Cannot await in the body or the initializer of a 'fixed' statement + Cannot await in context of a 'fixed' statement + Cannot await in context of a 'fixed' statement diff --git a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs index f1168bd86b221..20fc9d4a0cb59 100644 --- a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs +++ b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs @@ -14915,7 +14915,7 @@ static async Task M(int[] a) var expectedDiagnostics = new[] { - // (10,17): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (10,17): error CS9396: Cannot await in context of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 17), }; @@ -14954,13 +14954,13 @@ static async Task M(int[] a) // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 16), - // (8,13): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (8,13): error CS9396: Cannot await in context of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13)); var expectedDiagnostics = new[] { - // (8,13): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (8,13): error CS9396: Cannot await in context of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13), }; @@ -14997,13 +14997,13 @@ static async Task M() // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16), - // (6,26): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (6,26): error CS9396: Cannot await in context of a 'fixed' statement // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 26)); var expectedDiagnostics = new[] { - // (6,26): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (6,26): error CS9396: Cannot await in context of a 'fixed' statement // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 26), }; @@ -15040,19 +15040,19 @@ static async Task M() // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16), - // (6,27): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (6,27): error CS9396: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 27), - // (6,50): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (6,50): error CS9396: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 50)); var expectedDiagnostics = new[] { - // (6,27): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (6,27): error CS9396: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 27), - // (6,50): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (6,50): error CS9396: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 50), }; @@ -15093,13 +15093,13 @@ static async Task M(int[] a) // (7,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 16), - // (10,13): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (10,13): error CS9396: Cannot await in context of a 'fixed' statement // await f(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13)); var expectedDiagnostics = new[] { - // (10,13): error CS9396: Cannot await in the body or the initializer of a 'fixed' statement + // (10,13): error CS9396: Cannot await in context of a 'fixed' statement // await f(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13), }; From 992fcab57966ae3cb536cb9bbe2d338d228a9017 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 4 Aug 2026 14:48:19 +0200 Subject: [PATCH 8/9] Improve assert message --- src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs b/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs index 9ab00b1f838a6..65a7d4ce87140 100644 --- a/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs +++ b/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs @@ -143,7 +143,7 @@ private static void AssertExpectedMessageArgumentsLength(CommonMessageProvider m bits[value] = true; } } - Debug.Assert(expectedLength == actualLength); + Debug.Assert(expectedLength == actualLength, $"Expected {expectedLength} arguments for error code {errorCode}, but got {actualLength}. Message: {message}"); Debug.Assert(bits == BitVector.AllSet(actualLength)); #endif } From 72c68e4009c3aaedc7120cdacc9b2426e802a314 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 4 Aug 2026 14:51:48 +0200 Subject: [PATCH 9/9] Renumber error after merge --- .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../Test/CSharp15/UnsafeEvolutionTests.cs | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index a858624a520db..4d64076c4f9b0 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2505,10 +2505,10 @@ internal enum ErrorCode ERR_NoBreakId = 9393, ERR_NoContinueId = 9394, ERR_ClosedBadDerivedTypesProperty = 9395, - ERR_BadAwaitInFixed = 9396, ERR_InvalidPropertyUnsafeMods = 9396, ERR_SamePropertyUnsafeAccessorMods = 9397, + ERR_BadAwaitInFixed = 9398, // Note: you will need to do the following after adding errors: // 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) diff --git a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs index 5ecb755a37c60..3540ede1b083d 100644 --- a/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs +++ b/src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs @@ -15222,7 +15222,7 @@ static async Task M(int[] a) var expectedDiagnostics = new[] { - // (10,17): error CS9396: Cannot await in context of a 'fixed' statement + // (10,17): error CS9398: Cannot await in context of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 17), }; @@ -15261,13 +15261,13 @@ static async Task M(int[] a) // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 16), - // (8,13): error CS9396: Cannot await in context of a 'fixed' statement + // (8,13): error CS9398: Cannot await in context of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13)); var expectedDiagnostics = new[] { - // (8,13): error CS9396: Cannot await in context of a 'fixed' statement + // (8,13): error CS9398: Cannot await in context of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13), }; @@ -15304,13 +15304,13 @@ static async Task M() // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16), - // (6,26): error CS9396: Cannot await in context of a 'fixed' statement + // (6,26): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 26)); var expectedDiagnostics = new[] { - // (6,26): error CS9396: Cannot await in context of a 'fixed' statement + // (6,26): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 26), }; @@ -15347,19 +15347,19 @@ static async Task M() // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16), - // (6,27): error CS9396: Cannot await in context of a 'fixed' statement + // (6,27): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 27), - // (6,50): error CS9396: Cannot await in context of a 'fixed' statement + // (6,50): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 50)); var expectedDiagnostics = new[] { - // (6,27): error CS9396: Cannot await in context of a 'fixed' statement + // (6,27): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 27), - // (6,50): error CS9396: Cannot await in context of a 'fixed' statement + // (6,50): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 50), }; @@ -15400,13 +15400,13 @@ static async Task M(int[] a) // (7,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 16), - // (10,13): error CS9396: Cannot await in context of a 'fixed' statement + // (10,13): error CS9398: Cannot await in context of a 'fixed' statement // await f(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13)); var expectedDiagnostics = new[] { - // (10,13): error CS9396: Cannot await in context of a 'fixed' statement + // (10,13): error CS9398: Cannot await in context of a 'fixed' statement // await f(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13), };