diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md
index 165de2c1cb506..6734c34eaa7e4 100644
--- a/docs/Language Feature Status.md
+++ b/docs/Language Feature Status.md
@@ -17,6 +17,7 @@ efforts behind them.
| [Roles/Extensions](https://github.com/dotnet/csharplang/issues/5497) | [roles](https://github.com/dotnet/roslyn/tree/features/roles) | [In Progress](https://github.com/dotnet/roslyn/issues/66722) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [jjonescz](https://github.com/jjonescz) | | [MadsTorgersen](https://github.com/MadsTorgersen) |
| [Escape character](https://github.com/dotnet/csharplang/issues/7400) | N/A | [In Progress](https://github.com/dotnet/roslyn/pull/70497) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jcouv](https://github.com/jcouv), [RikkiGibson](https://github.com/RikkiGibson) | | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) |
| [Method group natural type improvements](https://github.com/dotnet/csharplang/blob/main/proposals/method-group-natural-type-improvements.md) | main | In Progress | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | | [jcouv](https://github.com/jcouv) |
+| [Native lock](https://github.com/dotnet/csharplang/issues/7104) | main | In Progress | [jjonescz](https://github.com/jjonescz) | | | [stephentoub](https://github.com/stephentoub) |
| Implicit indexer access in object initializers | main | [Merged into 17.9p3](https://github.com/dotnet/roslyn/pull/70649) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | | |
# C# 12.0
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
index c5290f1ed83eb..fa8acbaf3bbc9 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
@@ -279,6 +279,13 @@ BoundExpression createConversion(
if (!hasErrors && conversion.Exists)
{
ensureAllUnderlyingConversionsChecked(syntax, source, conversion, wasCompilerGenerated, destination, diagnostics);
+
+ if (conversion.Kind == ConversionKind.ImplicitReference &&
+ source.Type is { } sourceType &&
+ sourceType.IsWellKnownTypeLock())
+ {
+ diagnostics.Add(ErrorCode.WRN_ConvertingLock, source.Syntax);
+ }
}
return new BoundConversion(
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
index 5283dc51d063b..b3a6ff711dbcc 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
@@ -3182,13 +3182,14 @@ private BoundExpression BindOutVariableDeclarationArgument(
///
/// Reports an error when a bad special by-ref local was found.
///
- internal static void CheckRestrictedTypeInAsyncMethod(Symbol containingSymbol, TypeSymbol type, BindingDiagnosticBag diagnostics, SyntaxNode syntax, bool forUsingExpression = false)
+ internal static void CheckRestrictedTypeInAsyncMethod(Symbol containingSymbol, TypeSymbol type, BindingDiagnosticBag diagnostics, SyntaxNode syntax, ErrorCode errorCode = ErrorCode.ERR_BadSpecialByRefLocal)
{
+ Debug.Assert(errorCode is ErrorCode.ERR_BadSpecialByRefLocal or ErrorCode.ERR_BadSpecialByRefUsing or ErrorCode.ERR_BadSpecialByRefLock);
if (containingSymbol.Kind == SymbolKind.Method
&& ((MethodSymbol)containingSymbol).IsAsync
&& type.IsRestrictedType())
{
- Error(diagnostics, forUsingExpression ? ErrorCode.ERR_BadSpecialByRefUsing : ErrorCode.ERR_BadSpecialByRefLocal, syntax, type);
+ Error(diagnostics, errorCode, syntax, type);
}
}
diff --git a/src/Compilers/CSharp/Portable/Binder/LockBinder.cs b/src/Compilers/CSharp/Portable/Binder/LockBinder.cs
index 7a050ef28f219..42170b9bb3608 100644
--- a/src/Compilers/CSharp/Portable/Binder/LockBinder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/LockBinder.cs
@@ -54,6 +54,16 @@ internal override BoundStatement BindLockStatementParts(BindingDiagnosticBag dia
Error(diagnostics, ErrorCode.ERR_LockNeedsReference, exprSyntax, exprType);
hasErrors = true;
}
+ else if (exprType.IsWellKnownTypeLock() &&
+ Compilation.GetWellKnownType(WellKnownType.System_Threading_Lock__Scope) is { } scopeType)
+ {
+ CheckRestrictedTypeInAsyncMethod(
+ originalBinder.ContainingMemberOrLambda,
+ scopeType,
+ diagnostics,
+ exprSyntax,
+ errorCode: ErrorCode.ERR_BadSpecialByRefLock);
+ }
BoundStatement stmt = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics);
Debug.Assert(this.Locals.IsDefaultOrEmpty);
diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs
index 02b186c91a763..d1b9d601166b8 100644
--- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs
@@ -119,7 +119,7 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo
Debug.Assert(expressionOpt is not null);
if (expressionOpt.Type is not null)
{
- CheckRestrictedTypeInAsyncMethod(originalBinder.ContainingMemberOrLambda, expressionOpt.Type, diagnostics, expressionOpt.Syntax, forUsingExpression: true);
+ CheckRestrictedTypeInAsyncMethod(originalBinder.ContainingMemberOrLambda, expressionOpt.Type, diagnostics, expressionOpt.Syntax, errorCode: ErrorCode.ERR_BadSpecialByRefUsing);
}
}
else
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 93093f92a9b20..fc4adc5ba7a92 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -7839,4 +7839,13 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
implicit indexer initializer
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
\ No newline at end of file
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
index e3e945e05b48e..a90ade9c09573 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -2285,6 +2285,9 @@ internal enum ErrorCode
#endregion
+ WRN_ConvertingLock = 9214,
+ ERR_BadSpecialByRefLock = 9215,
+
// Note: you will need to do the following after adding warnings:
// 1) Re-generate compiler code (eng\generate-compiler-code.cmd).
}
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
index a40daa23293fa..aa818b6b54cd8 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
@@ -2411,6 +2411,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code)
case ErrorCode.ERR_InvalidExperimentalDiagID:
case ErrorCode.ERR_SpreadMissingMember:
case ErrorCode.ERR_CollectionExpressionTargetNoElementType:
+ case ErrorCode.ERR_BadSpecialByRefLock:
return false;
default:
// NOTE: All error codes must be explicitly handled in this switch statement
diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs
index c8feebec6fef9..b07702f8e708f 100644
--- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs
+++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs
@@ -337,6 +337,7 @@ public static bool IsWarning(ErrorCode code)
case ErrorCode.WRN_Experimental:
case ErrorCode.WRN_CollectionExpressionRefStructMayAllocate:
case ErrorCode.WRN_CollectionExpressionRefStructSpreadMayAllocate:
+ case ErrorCode.WRN_ConvertingLock:
return true;
default:
return false;
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LockStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LockStatement.cs
index a12604751d8b0..f2e40d0e0c7e3 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LockStatement.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LockStatement.cs
@@ -13,8 +13,11 @@ namespace Microsoft.CodeAnalysis.CSharp
internal sealed partial class LocalRewriter
{
///
- /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit
- /// before and after the body, respectively.
+ /// Lowers a lock statement to a try-finally block that calls (before and after the body, respectively):
+ ///
+ /// Lock.EnterLockScope and Lock+Scope.Dispose if the argument is of type Lock, or
+ /// Monitor.Enter and Monitor.Exit.
+ ///
///
public override BoundNode VisitLockStatement(BoundLockStatement node)
{
@@ -37,6 +40,41 @@ public override BoundNode VisitLockStatement(BoundLockStatement node)
argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp.
}
+ if (argumentType.IsWellKnownTypeLock() &&
+ TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Lock__EnterLockScope, out MethodSymbol enterLockScope) &&
+ TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Lock__Scope__Dispose, out MethodSymbol lockScopeDispose))
+ {
+ // lock (x) { body } -> using (x.EnterLockScope()) { body }
+
+ var tryBlock = rewrittenBody is BoundBlock block ? block : BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody);
+
+ var enterLockScopeCall = BoundCall.Synthesized(
+ rewrittenArgument.Syntax,
+ rewrittenArgument,
+ initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown,
+ enterLockScope);
+
+ BoundLocal boundTemp = _factory.StoreToTemp(enterLockScopeCall,
+ out BoundAssignmentOperator tempAssignment,
+ syntaxOpt: rewrittenArgument.Syntax,
+ kind: SynthesizedLocalKind.Using);
+ var expressionStatement = new BoundExpressionStatement(rewrittenArgument.Syntax, tempAssignment);
+
+ BoundStatement tryFinally = RewriteUsingStatementTryFinally(
+ rewrittenArgument.Syntax,
+ rewrittenArgument.Syntax,
+ tryBlock,
+ boundTemp,
+ awaitKeywordOpt: default,
+ awaitOpt: null,
+ patternDisposeInfo: MethodArgumentInfo.CreateParameterlessMethod(lockScopeDispose));
+
+ return new BoundBlock(
+ lockSyntax,
+ locals: [boundTemp.LocalSymbol],
+ statements: [expressionStatement, tryFinally]);
+ }
+
if (argumentType.Kind == SymbolKind.TypeParameter)
{
// If the argument has a type parameter type, then we'll box it right away
diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
index 98b3b953330d2..aeb15b3ee2a26 100644
--- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
@@ -2078,6 +2078,12 @@ internal static bool IsWellKnownTypeIsExternalInit(this TypeSymbol typeSymbol)
internal static bool IsWellKnownTypeOutAttribute(this TypeSymbol typeSymbol) => typeSymbol.IsWellKnownInteropServicesTopLevelType("OutAttribute");
+ internal static bool IsWellKnownTypeLock(this TypeSymbol typeSymbol)
+ {
+ return typeSymbol.Name == "Lock" && typeSymbol.ContainingType is null &&
+ typeSymbol.IsContainedInNamespace("System", "Threading");
+ }
+
private static bool IsWellKnownInteropServicesTopLevelType(this TypeSymbol typeSymbol, string name)
{
if (typeSymbol.Name != name || typeSymbol.ContainingType is object)
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
index 240fe2b351e65..9bb8079ffb814 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
@@ -267,6 +267,11 @@
Člen záznamu {0} musí být čitelná vlastnost instance nebo pole typu {1}, která se bude shodovat s pozičním parametrem {2}.
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ Prostředek použitého příkazu typu {0} nejde použít v asynchronních metodách ani v asynchronních výrazech lambda.
@@ -2562,6 +2567,16 @@
Operace může při běhu přetéct (pro přepis použijte syntaxi unchecked)
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ Porovnání ukazatelů funkcí může přinést neočekávaný výsledek, protože ukazatele na stejnou funkci můžou být rozdílné.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
index d0817c17f3183..436f9fd460416 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
@@ -267,6 +267,11 @@
Das Datensatzelement "{0}" muss eine lesbare Instanzeigenschaft oder ein Feld vom Typ "{1}" sein, um dem Positionsparameter "{2}" zu entsprechen.
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ Eine using-Anweisungsressource vom Typ „{0}“ kann nicht in asynchronen Methoden oder asynchronen Lambdaausdrücken verwendet werden.
@@ -2562,6 +2567,16 @@
Der Vorgang kann zur Laufzeit überlaufen (verwenden Sie zum Überschreiben die Syntax „unchecked“)
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ Der Vergleich von Funktionszeigern kann zu einem unerwarteten Ergebnis führen, weil Zeiger auf dieselbe Funktion möglicherweise unterschiedlich sind.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
index 0d4ff8080e772..b45139462488b 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
@@ -267,6 +267,11 @@
El miembro de registro '{0}' debe ser una propiedad de instancia legible o un campo de tipo '{1}' para coincidir con el parámetro posicional '{2}'.
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ Un recurso de instrucción using de tipo '{0}' no se puede usar en métodos asincrónicos ni expresiones lambda asincrónicas.
@@ -2562,6 +2567,16 @@
La operación puede desbordarse en tiempo de ejecución (use la sintaxis "sin activar" para invalidarla).
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ La comparación de los punteros de función puede proporcionar resultados inesperados, ya que los punteros a la misma función pueden ser distintos.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
index b10c7f7c26029..71059c09abae0 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
@@ -267,6 +267,11 @@
Le membre d'enregistrement '{0}' doit être une propriété d'instance our champ lisible de type '{1}' pour correspondre au paramètre positionnel '{2}'.
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ Une ressource d’instruction d’utilisation de type '{0}' ne peut pas être utilisée dans des méthodes asynchrones ou des expressions lambda asynchrones.
@@ -2562,6 +2567,16 @@
L'opération peut déborder au moment de l'exécution (utilisez la syntaxe 'unchecked' pour passer outre).
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ La comparaison des pointeurs de fonction peut donner un résultat inattendu, car les pointeurs vers la même fonction peuvent être distincts.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
index 98b232aabc958..e0dc685553012 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
@@ -267,6 +267,11 @@
Il membro di record '{0}' deve essere una proprietà di istanza leggibile o campo di tipo '{1}' per corrispondere al parametro posizionale '{2}'.
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ Non è possibile usare una risorsa di istruzione using di tipo '{0}' in metodi asincroni o espressioni lambda asincrone.
@@ -2562,6 +2567,16 @@
Con l’operazione può verificarsi un overflow in fase di esecuzione. Usare la sintassi 'unchecked' per eseguire l'override
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ Il confronto dei puntatori a funzione potrebbe produrre un risultato imprevisto perché i puntatori alla stessa funzione possono essere distinti.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
index 822c0095ea75a..3e92efeed3188 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
@@ -267,6 +267,11 @@
レコード メンバー '{0}' は、位置指定パラメーター '{2}' に一致させるための型 '{1}' の読み取り可能なインスタンス プロパティまたはフィールドである必要があります。
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ 型 '{0}' の using ステートメント リソースは、非同期メソッドまたは非同期ラムダ式では使用できません。
@@ -2562,6 +2567,16 @@
実行時に操作がオーバーフローする可能性があります (オーバーライドするには 'unchecked' 構文を使用してください)
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ 同じ関数へのポインターがそれぞれ異なっている可能性があるため、関数ポインターの比較によって予期しない結果が生成されるおそれがあります。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
index a9e2b312efb71..2e13f3f071629 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
@@ -267,6 +267,11 @@
위치 매개 변수 '{0}'과(와) 일치하려면 레코드 멤버 '{1}'이(가) 유형 '{2}'의 읽을 수 있는 인스턴스 속성 또는 필드여야 합니다.
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ '{0}' 형식의 using 문 리소스는 비동기 메서드 또는 비동기 람다 식에 사용할 수 없습니다.
@@ -2562,6 +2567,16 @@
작업이 런타임에 오버플로될 수 있습니다('선택되지 않은' 구문을 사용하여 재정의).
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ 같은 함수에 대한 포인터가 다를 수 있으므로 함수 포인터를 비교하면 예기치 않은 결과가 발생할 수 있습니다.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
index 386d274db47af..16a9a59b3a7be 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
@@ -267,6 +267,11 @@
Składowa rekordu "{0}" musi być możliwą do odczytu właściwością wystąpienia typu "{1}", aby dopasować parametr pozycyjny "{2}".
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ Zasobu instrukcji przy użyciu typu '{0}' nie można używać w metodach asynchronicznych ani asynchronicznych wyrażeniach lambda.
@@ -2562,6 +2567,16 @@
Operacja może się przepełnić w środowisku uruchomieniowym (użyj składni „niezaznaczone”, aby zastąpić)
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ Porównanie wskaźników funkcji może zwrócić nieoczekiwany wynik, ponieważ wskaźniki do tej samej funkcji mogą być różne.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
index 6b0fc279fd701..f1b1c45470dae 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
@@ -267,6 +267,11 @@
O membro do registro '{0}' precisa ser uma propriedade de instância legível ou campo do tipo '{1}' para corresponder ao parâmetro posicional '{2}'.
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ Um recurso de instrução using do tipo "{0}" não pode ser usado em métodos assíncronos ou expressões lambda assíncronas.
@@ -2562,6 +2567,16 @@
A operação pode estourar em tempo de execução (use a sintaxe 'não verificada' para substituir)
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ A comparação de ponteiros de função pode gerar um resultado inesperado, pois os ponteiros para a mesma função podem ser diferentes.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
index 9ca32982c8ac4..1bd1985074d60 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
@@ -267,6 +267,11 @@
Элемент записи "{0}" должен быть доступным для чтения свойством экземпляра или полем типа "{1}", чтобы соответствовать позиционному параметру "{2}".
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ Ресурс оператора использования типа "{0}" нельзя применять в асинхронных методах или асинхронных лямбда-выражениях.
@@ -2562,6 +2567,16 @@
Операция может привести к переполнению в среде выполнения (для переопределения используйте синтаксис "unchecked")
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ Сравнение указателей на функции может привести к непредвиденному результату, так как указатели на одну и ту же функцию могут быть разными.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
index d66953dd93cef..156edc3464ae6 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
@@ -267,6 +267,11 @@
{0} kayıt üyesi, {1} konumsal parametresi ile eşleşmesi için {2} türünde okunabilir bir örnek özelliği veya alan olmalıdır.
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ '{0}' türündeki bir using deyimi kaynağı, asenkron yöntemlerde veya asenkron lambda ifadelerinde kullanılamaz.
@@ -2562,6 +2567,16 @@
İşlem, çalışma zamanında taşabilir (geçersiz kılmak için “denetlenmemiş” sözdizimini kullanın)
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ Aynı işleve yönelik işaretçiler birbirinden farklı olabileceğinden işlev işaretçilerinin karşılaştırılması beklenmeyen bir sonuç verebilir.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
index 12b13ef6c50e3..52daaf7455723 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
@@ -267,6 +267,11 @@
记录成员 '{0}' 必须为类型 '{1}' 的可读实例属性或字段,以匹配位置参数 '{2}'。
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ 无法在异步方法或异步 lambda 表达式中使用类型为“{0}”的 using 语句资源。
@@ -2562,6 +2567,16 @@
操作可能在运行时溢出(请使用“unchecked”语法替代)
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ 函数指针比较可能产生意外的结果,因为指向同一函数的指针可能是不同的。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
index cd2428ed9b358..8249dbab0bad0 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
@@ -267,6 +267,11 @@
記錄成員 '{0}' 必須是類型 '{1}' 的可讀取執行個體屬性或欄位,才能符合位置參數 '{2}'。
+
+
+ A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions.
+
+ 類型 '{0}' 的 using 陳述式資源不能用於非同步方法或非同步 Lambda 運算式。
@@ -2562,6 +2567,16 @@
作業在執行階段可能會溢位 (請使用 'unchecked' 語法覆寫)
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+
+
+
+ A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+
+ 因為同一個函式的指標可能截然不同,所以比較函式指標可能會產生非預期的結果。
diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs
new file mode 100644
index 0000000000000..1ba638450d5b5
--- /dev/null
+++ b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs
@@ -0,0 +1,1079 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
+using Microsoft.CodeAnalysis.Test.Utilities;
+using Xunit;
+
+namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics;
+
+public class LockTests : CSharpTestBase
+{
+ private const string LockTypeDefinition = """
+ namespace System.Threading
+ {
+ public class Lock
+ {
+ public Scope EnterLockScope()
+ {
+ Console.Write("E");
+ return new Scope();
+ }
+
+ public ref struct Scope
+ {
+ public void Dispose()
+ {
+ Console.Write("D");
+ }
+ }
+ }
+ }
+ """;
+
+ [Fact]
+ public void LockVsUsing()
+ {
+ var source = """
+ using System;
+ using System.Threading;
+
+ static class C
+ {
+ static readonly Lock _lock = new();
+
+ static void Main()
+ {
+ M1();
+ M2();
+ }
+
+ static void M1()
+ {
+ Console.Write("1");
+ lock (_lock)
+ {
+ Console.Write("2");
+ }
+ Console.Write("3");
+ }
+
+ static void M2()
+ {
+ Console.Write("1");
+ using (_lock.EnterLockScope())
+ {
+ Console.Write("2");
+ }
+ Console.Write("3");
+ }
+ }
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], expectedOutput: "1E2D31E2D3",
+ verify: Verification.FailsILVerify);
+ verifier.VerifyDiagnostics();
+ var il = """
+ {
+ // Code size 52 (0x34)
+ .maxstack 1
+ .locals init (System.Threading.Lock.Scope V_0)
+ IL_0000: ldstr "1"
+ IL_0005: call "void System.Console.Write(string)"
+ IL_000a: ldsfld "System.Threading.Lock C._lock"
+ IL_000f: callvirt "System.Threading.Lock.Scope System.Threading.Lock.EnterLockScope()"
+ IL_0014: stloc.0
+ .try
+ {
+ IL_0015: ldstr "2"
+ IL_001a: call "void System.Console.Write(string)"
+ IL_001f: leave.s IL_0029
+ }
+ finally
+ {
+ IL_0021: ldloca.s V_0
+ IL_0023: call "void System.Threading.Lock.Scope.Dispose()"
+ IL_0028: endfinally
+ }
+ IL_0029: ldstr "3"
+ IL_002e: call "void System.Console.Write(string)"
+ IL_0033: ret
+ }
+ """;
+ verifier.VerifyIL("C.M2", il);
+ verifier.VerifyIL("C.M1", il);
+ }
+
+ [Fact]
+ public void MissingEnterLockScope()
+ {
+ var source = """
+ System.Threading.Lock l = new();
+ lock (l) { }
+
+ namespace System.Threading
+ {
+ public class Lock { }
+ }
+ """;
+ CreateCompilation(source).VerifyEmitDiagnostics(
+ // (2,1): error CS0656: Missing compiler required member 'System.Threading.Lock.EnterLockScope'
+ // lock (l) { }
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "lock (l) { }").WithArguments("System.Threading.Lock", "EnterLockScope").WithLocation(2, 1));
+ }
+
+ [Fact]
+ public void EnterLockScopeReturnsVoid()
+ {
+ var source = """
+ System.Threading.Lock l = new();
+ lock (l) { }
+
+ namespace System.Threading
+ {
+ public class Lock
+ {
+ public void EnterLockScope() { }
+ }
+ }
+ """;
+ CreateCompilation(source).VerifyEmitDiagnostics(
+ // (2,1): error CS0656: Missing compiler required member 'System.Threading.Lock.EnterLockScope'
+ // lock (l) { }
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "lock (l) { }").WithArguments("System.Threading.Lock", "EnterLockScope").WithLocation(2, 1));
+ }
+
+ [Fact]
+ public void EnterLockScopeTakesArguments()
+ {
+ var source = """
+ System.Threading.Lock l = new();
+ lock (l) { }
+
+ namespace System.Threading
+ {
+ public class Lock
+ {
+ public Scope EnterLockScope(int arg) => new Scope();
+
+ public ref struct Scope
+ {
+ public void Dispose() { }
+ }
+ }
+ }
+ """;
+ CreateCompilation(source).VerifyEmitDiagnostics(
+ // (2,1): error CS0656: Missing compiler required member 'System.Threading.Lock.EnterLockScope'
+ // lock (l) { }
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "lock (l) { }").WithArguments("System.Threading.Lock", "EnterLockScope").WithLocation(2, 1));
+ }
+
+ [Fact]
+ public void MissingScopeDispose()
+ {
+ var source = """
+ System.Threading.Lock l = new();
+ lock (l) { }
+
+ namespace System.Threading
+ {
+ public class Lock
+ {
+ public Scope EnterLockScope() => new Scope();
+
+ public struct Scope { }
+ }
+ }
+ """;
+ CreateCompilation(source).VerifyEmitDiagnostics(
+ // (2,1): error CS0656: Missing compiler required member 'System.Threading.Lock+Scope.Dispose'
+ // lock (l) { }
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "lock (l) { }").WithArguments("System.Threading.Lock+Scope", "Dispose").WithLocation(2, 1));
+ }
+
+ [Fact]
+ public void ScopeDisposeReturnsNonVoid()
+ {
+ var source = """
+ System.Threading.Lock l = new();
+ lock (l) { }
+
+ namespace System.Threading
+ {
+ public class Lock
+ {
+ public Scope EnterLockScope() => new Scope();
+
+ public ref struct Scope
+ {
+ public int Dispose() => 1;
+ }
+ }
+ }
+ """;
+ CreateCompilation(source).VerifyEmitDiagnostics(
+ // (2,1): error CS0656: Missing compiler required member 'System.Threading.Lock+Scope.Dispose'
+ // lock (l) { }
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "lock (l) { }").WithArguments("System.Threading.Lock+Scope", "Dispose").WithLocation(2, 1));
+ }
+
+ [Fact]
+ public void ScopeDisposeTakesArguments()
+ {
+ var source = """
+ System.Threading.Lock l = new();
+ lock (l) { }
+
+ namespace System.Threading
+ {
+ public class Lock
+ {
+ public Scope EnterLockScope() => new Scope();
+
+ public ref struct Scope
+ {
+ public void Dispose(int x) { }
+ }
+ }
+ }
+ """;
+ CreateCompilation(source).VerifyEmitDiagnostics(
+ // (2,1): error CS0656: Missing compiler required member 'System.Threading.Lock+Scope.Dispose'
+ // lock (l) { }
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "lock (l) { }").WithArguments("System.Threading.Lock+Scope", "Dispose").WithLocation(2, 1));
+ }
+
+ [Fact]
+ public void ExternalAssembly()
+ {
+ var lib = CreateCompilation(LockTypeDefinition)
+ .VerifyDiagnostics()
+ .EmitToImageReference();
+ var source = """
+ using System;
+ using System.Threading;
+
+ Lock l = new Lock();
+ lock (l) { Console.Write("L"); }
+ """;
+ var verifier = CompileAndVerify(source, [lib], expectedOutput: "ELD");
+ verifier.VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void InPlace()
+ {
+ var source = """
+ using System;
+ using System.Threading;
+
+ lock (new Lock())
+ {
+ Console.Write("L");
+ }
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify,
+ expectedOutput: "ELD");
+ verifier.VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void EmbeddedStatement()
+ {
+ var source = """
+ using System;
+ using System.Threading;
+
+ lock (new Lock()) Console.Write("L");
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify,
+ expectedOutput: "ELD");
+ verifier.VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void EmptyStatement()
+ {
+ var source = """
+ using System.Threading;
+
+ lock (new Lock()) ;
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify,
+ expectedOutput: "ED");
+ verifier.VerifyDiagnostics(
+ // (3,19): warning CS0642: Possible mistaken empty statement
+ // lock (new Lock()) ;
+ Diagnostic(ErrorCode.WRN_PossibleMistakenNullStatement, ";").WithLocation(3, 19));
+ }
+
+ [Fact]
+ public void Nullable()
+ {
+ var source = """
+ #nullable enable
+ using System;
+ using System.Threading;
+
+ static class C
+ {
+ static void Main()
+ {
+ M(new Lock());
+ }
+
+ static void M(Lock? l)
+ {
+ lock (l) { Console.Write("1"); }
+ lock (l) { Console.Write("2"); }
+ }
+ }
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify,
+ expectedOutput: "E1DE2D");
+ verifier.VerifyDiagnostics(
+ // (14,15): warning CS8602: Dereference of a possibly null reference.
+ // lock (l) { Console.Write("1"); }
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "l").WithLocation(14, 15));
+ }
+
+ [Theory, CombinatorialData]
+ public void Null([CombinatorialValues("null", "default")] string expr)
+ {
+ var source = $$"""
+ #nullable enable
+ static class C
+ {
+ static void Main()
+ {
+ try
+ {
+ M();
+ }
+ catch (System.NullReferenceException)
+ {
+ System.Console.Write("caught");
+ }
+ }
+ static void M()
+ {
+ lock ((System.Threading.Lock){{expr}}) { }
+ }
+ }
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify,
+ expectedOutput: "caught");
+ verifier.VerifyDiagnostics(
+ // (17,15): warning CS8600: Converting null literal or possible null value to non-nullable type.
+ // lock ((System.Threading.Lock)null) { }
+ Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, $"(System.Threading.Lock){expr}").WithLocation(17, 15),
+ // (17,15): warning CS8602: Dereference of a possibly null reference.
+ // lock ((System.Threading.Lock)null) { }
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, $"(System.Threading.Lock){expr}").WithLocation(17, 15));
+ verifier.VerifyIL("C.M", """
+ {
+ // Code size 18 (0x12)
+ .maxstack 1
+ .locals init (System.Threading.Lock.Scope V_0)
+ IL_0000: ldnull
+ IL_0001: callvirt "System.Threading.Lock.Scope System.Threading.Lock.EnterLockScope()"
+ IL_0006: stloc.0
+ .try
+ {
+ IL_0007: leave.s IL_0011
+ }
+ finally
+ {
+ IL_0009: ldloca.s V_0
+ IL_000b: call "void System.Threading.Lock.Scope.Dispose()"
+ IL_0010: endfinally
+ }
+ IL_0011: ret
+ }
+ """);
+ }
+
+ [Fact]
+ public void Await()
+ {
+ var source = """
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ lock (new Lock())
+ {
+ await Task.Yield();
+ }
+ """;
+ CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics(
+ // (4,7): error CS9215: A lock statement scope type 'Lock.Scope' cannot be used in async methods or async lambda expressions.
+ // lock (new Lock())
+ Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(4, 7),
+ // (6,5): error CS1996: Cannot await in the body of a lock statement
+ // await Task.Yield();
+ Diagnostic(ErrorCode.ERR_BadAwaitInLock, "await Task.Yield()").WithLocation(6, 5));
+ }
+
+ [Fact]
+ public void AsyncMethod()
+ {
+ var source = """
+ #pragma warning disable 1998 // async method lacks 'await' operators
+ using System.Threading;
+
+ class C
+ {
+ async void M()
+ {
+ lock (new Lock()) { }
+ }
+ }
+ """;
+ CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics(
+ // (8,15): error CS9215: A lock statement scope type 'Lock.Scope' cannot be used in async methods or async lambda expressions.
+ // lock (new Lock()) { }
+ Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(8, 15));
+ }
+
+ [Fact]
+ public void AsyncMethod_WithAwait()
+ {
+ var source = """
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ class C
+ {
+ async void M()
+ {
+ await Task.Yield();
+ lock (new Lock()) { }
+ }
+ }
+ """;
+ CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics(
+ // (9,15): error CS9215: A lock statement scope type 'Lock.Scope' cannot be used in async methods or async lambda expressions.
+ // lock (new Lock()) { }
+ Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15));
+ }
+
+ [Fact]
+ public void AsyncLocalFunction()
+ {
+ var source = """
+ #pragma warning disable 1998 // async method lacks 'await' operators
+ using System.Threading;
+
+ async void local()
+ {
+ lock (new Lock()) { }
+ }
+
+ local();
+ """;
+ CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics(
+ // (6,11): error CS9215: A lock statement scope type 'Lock.Scope' cannot be used in async methods or async lambda expressions.
+ // lock (new Lock()) { }
+ Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(6, 11));
+ }
+
+ [Fact]
+ public void AsyncLocalFunction_WithAwait()
+ {
+ var source = """
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ async void local()
+ {
+ await Task.Yield();
+ lock (new Lock()) { }
+ }
+
+ local();
+ """;
+ CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics(
+ // (7,11): error CS9215: A lock statement scope type 'Lock.Scope' cannot be used in async methods or async lambda expressions.
+ // lock (new Lock()) { }
+ Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(7, 11));
+ }
+
+ [Fact]
+ public void AsyncLambda()
+ {
+ var source = """
+ #pragma warning disable 1998 // async method lacks 'await' operators
+ using System.Threading;
+
+ var lam = async () =>
+ {
+ lock (new Lock()) { }
+ };
+ """;
+ CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics(
+ // (6,11): error CS9215: A lock statement scope type 'Lock.Scope' cannot be used in async methods or async lambda expressions.
+ // lock (new Lock()) { }
+ Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(6, 11));
+ }
+
+ [Fact]
+ public void AsyncLambda_WithAwait()
+ {
+ var source = """
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ var lam = async () =>
+ {
+ await Task.Yield();
+ lock (new Lock()) { }
+ };
+ """;
+ CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics(
+ // (7,11): error CS9215: A lock statement scope type 'Lock.Scope' cannot be used in async methods or async lambda expressions.
+ // lock (new Lock()) { }
+ Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(7, 11));
+ }
+
+ [Fact]
+ public void Yield()
+ {
+ var source = """
+ using System.Collections.Generic;
+ using System.Threading;
+
+ class C
+ {
+ IEnumerable M()
+ {
+ yield return 1;
+ lock (new Lock())
+ {
+ yield return 2;
+ }
+ yield return 3;
+ }
+ }
+ """;
+ CreateCompilation([source, LockTypeDefinition]).VerifyEmitDiagnostics(
+ // (9,15): error CS4013: Instance of type 'Lock.Scope' cannot be used inside a nested function, query expression, iterator block or async method
+ // lock (new Lock())
+ Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15));
+ }
+
+ [Theory, CombinatorialData]
+ public void CastToObject([CombinatorialValues("object ", "dynamic")] string type)
+ {
+ var source = $$"""
+ using System;
+ using System.Threading;
+
+ Lock l = new();
+
+ {{type}} o = l;
+ lock (o) { Console.Write("1"); }
+
+ lock (({{type}})l) { Console.Write("2"); }
+
+ lock (l as {{type}}) { Console.Write("3"); }
+
+ o = l as {{type}};
+ lock (o) { Console.Write("4"); }
+
+ static {{type}} Cast1(T t) => t;
+ lock (Cast1(l)) { Console.Write("5"); }
+
+ static {{type}} Cast2(T t) where T : class => t;
+ lock (Cast2(l)) { Console.Write("6"); }
+
+ static {{type}} Cast3(T t) where T : Lock => t;
+ lock (Cast3(l)) { Console.Write("7"); }
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify,
+ expectedOutput: "1234567");
+ verifier.VerifyDiagnostics(
+ // 0.cs(6,13): warning CS9214: A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+ // object o = l;
+ Diagnostic(ErrorCode.WRN_ConvertingLock, "l").WithLocation(6, 13),
+ // 0.cs(9,16): warning CS9214: A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+ // lock ((object )l) { Console.Write("2"); }
+ Diagnostic(ErrorCode.WRN_ConvertingLock, "l").WithLocation(9, 16),
+ // 0.cs(11,7): warning CS9214: A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+ // lock (l as object ) { Console.Write("3"); }
+ Diagnostic(ErrorCode.WRN_ConvertingLock, "l").WithLocation(11, 7),
+ // 0.cs(13,5): warning CS9214: A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+ // o = l as object ;
+ Diagnostic(ErrorCode.WRN_ConvertingLock, "l").WithLocation(13, 5));
+ }
+
+ [Fact]
+ public void CommonType()
+ {
+ var source = """
+ using System;
+ using System.Threading;
+
+ var array1 = new[] { new Lock(), new Lock() };
+ Console.WriteLine(array1.GetType());
+
+ var array2 = new[] { new Lock(), new object() };
+ Console.WriteLine(array2.GetType());
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify, expectedOutput: """
+ System.Threading.Lock[]
+ System.Object[]
+ """);
+ verifier.VerifyDiagnostics(
+ // 0.cs(7,22): warning CS9214: A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+ // var array2 = new[] { new Lock(), new object() };
+ Diagnostic(ErrorCode.WRN_ConvertingLock, "new Lock()").WithLocation(7, 22));
+ }
+
+ [Theory, CombinatorialData]
+ public void CastToBase([CombinatorialValues("interface", "class")] string baseKind)
+ {
+ var source = $$"""
+ using System;
+ using System.Threading;
+
+ ILock l1 = new Lock();
+ lock (l1) { Console.Write("1"); }
+
+ ILock l2 = new Lock();
+ lock ((Lock)l2) { Console.Write("2"); }
+
+ namespace System.Threading
+ {
+ public {{baseKind}} ILock { }
+
+ public class Lock : ILock
+ {
+ public Scope EnterLockScope()
+ {
+ Console.Write("E");
+ return new Scope();
+ }
+
+ public ref struct Scope
+ {
+ public void Dispose()
+ {
+ Console.Write("D");
+ }
+ }
+ }
+ }
+ """;
+ var verifier = CompileAndVerify(source, verify: Verification.FailsILVerify,
+ expectedOutput: "1E2D");
+ verifier.VerifyDiagnostics(
+ // (4,12): warning CS9214: A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+ // ILock l1 = new Lock();
+ Diagnostic(ErrorCode.WRN_ConvertingLock, "new Lock()").WithLocation(4, 12),
+ // (7,12): warning CS9214: A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+ // ILock l2 = new Lock();
+ Diagnostic(ErrorCode.WRN_ConvertingLock, "new Lock()").WithLocation(7, 12));
+ }
+
+ [Fact]
+ public void DerivedLock()
+ {
+ var source = """
+ using System;
+ using System.Threading;
+
+ DerivedLock l1 = new DerivedLock();
+ lock (l1) { Console.Write("1"); }
+
+ Lock l2 = l1;
+ lock (l2) { Console.Write("2"); }
+
+ DerivedLock l3 = (DerivedLock)l2;
+ lock (l3) { Console.Write("3"); }
+
+ namespace System.Threading
+ {
+ public class Lock
+ {
+ public Scope EnterLockScope()
+ {
+ Console.Write("E");
+ return new Scope();
+ }
+
+ public ref struct Scope
+ {
+ public void Dispose()
+ {
+ Console.Write("D");
+ }
+ }
+ }
+
+ public class DerivedLock : Lock { }
+ }
+ """;
+ var verifier = CompileAndVerify(source, verify: Verification.FailsILVerify,
+ expectedOutput: "1E2D3");
+ // Note: no warnings here as we don't expect `Lock` to be unsealed,
+ // so this doesn't warrant a warning in spec and implementation.
+ verifier.VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void Downcast()
+ {
+ var source = """
+ using System;
+ using System.Threading;
+
+ object o = new Lock();
+ lock ((Lock)o) { Console.Write("L"); }
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify,
+ expectedOutput: "ELD");
+ verifier.VerifyDiagnostics(
+ // 0.cs(4,12): warning CS9214: A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement.
+ // object o = new Lock();
+ Diagnostic(ErrorCode.WRN_ConvertingLock, "new Lock()").WithLocation(4, 12));
+ }
+
+ [Fact]
+ public void OtherConversions()
+ {
+ var source = """
+ #nullable enable
+ using System.Threading;
+
+ I i = new Lock();
+ Lock? l = new Lock();
+ C c = new Lock();
+ D d = new Lock();
+ d = (D)new Lock();
+
+ interface I { }
+
+ class C
+ {
+ public static implicit operator C(Lock l) => new C();
+ }
+
+ class D
+ {
+ public static explicit operator D(Lock l) => new D();
+ }
+ """;
+ // No warnings about converting `Lock` expected.
+ CreateCompilation([source, LockTypeDefinition]).VerifyEmitDiagnostics(
+ // 0.cs(4,7): error CS0266: Cannot implicitly convert type 'System.Threading.Lock' to 'I'. An explicit conversion exists (are you missing a cast?)
+ // I i = new Lock();
+ Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "new Lock()").WithArguments("System.Threading.Lock", "I").WithLocation(4, 7),
+ // 0.cs(7,7): error CS0266: Cannot implicitly convert type 'System.Threading.Lock' to 'D'. An explicit conversion exists (are you missing a cast?)
+ // D d = new Lock();
+ Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "new Lock()").WithArguments("System.Threading.Lock", "D").WithLocation(7, 7));
+ }
+
+ [Theory]
+ [InlineData("")]
+ [InlineData("where T : class")]
+ [InlineData("where T : Lock")]
+ public void GenericParameter(string constraint)
+ {
+ var source = $$"""
+ using System;
+ using System.Threading;
+
+ M(new Lock());
+
+ static void M(T t) {{constraint}}
+ {
+ lock (t) { Console.Write("L"); }
+ }
+ """;
+ var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify,
+ expectedOutput: "L");
+ verifier.VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void GenericParameter_Object()
+ {
+ var source = """
+ using System;
+ using System.Threading;
+
+ M