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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 同じ関数へのポインターがそれぞれ異なっている可能性があるため、関数ポインターの比較によって予期しない結果が生成されるおそれがあります。 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 같은 함수에 대한 포인터가 다를 수 있으므로 함수 포인터를 비교하면 예기치 않은 결과가 발생할 수 있습니다. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. Сравнение указателей на функции может привести к непредвиденному результату, так как указатели на одну и ту же функцию могут быть разными. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 函数指针比较可能产生意外的结果,因为指向同一函数的指针可能是不同的。 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. + A lock statement scope type '{0}' cannot be used in async methods or async lambda expressions. + + A using statement resource of 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. + + + + 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. + + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. 因為同一個函式的指標可能截然不同,所以比較函式指標可能會產生非預期的結果。 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(new Lock()); + + static void M(T t) + { + lock (t) { Console.Write("L"); } + } + """; + var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify, + expectedOutput: "L"); + verifier.VerifyDiagnostics( + // 0.cs(4,11): warning CS9214: A value of type 'System.Threading.Lock' converted to another type will use likely unintended monitor-based locking in 'lock' statement. + // M(new Lock()); + Diagnostic(ErrorCode.WRN_ConvertingLock, "new Lock()").WithLocation(4, 11)); + } + + [Fact] + public void UseSiteError_EnterLockScope() + { + // namespace System.Threading + // { + // public class Lock + // { + // [System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute("Test")] + // public Scope EnterLockScope() => throw null; + // + // public ref struct Scope + // { + // public void Dispose() { } + // } + // } + // } + var ilSource = """ + .class public auto ansi sealed beforefieldinit System.Threading.Lock extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + + .method public hidebysig instance class System.Threading.Lock/Scope EnterLockScope () cil managed + { + .custom instance void System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = ( + 01 00 04 54 65 73 74 00 00 + ) + .maxstack 8 + ldnull + throw + } + + .class nested public sequential ansi sealed beforefieldinit Scope extends System.ValueType + { + .custom instance void System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + + .pack 0 + .size 1 + + .method public hidebysig instance void Dispose () cil managed + { + .maxstack 8 + ret + } + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor(string s) cil managed + { + .maxstack 8 + ret + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsByRefLikeAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + """; + var source = """ + class C + { + void M(System.Threading.Lock l) + { + lock (l) { } + } + } + """; + CreateCompilationWithIL(source, ilSource).VerifyEmitDiagnostics( + // (5,9): error CS9041: 'Lock.EnterLockScope()' requires compiler feature 'Test', which is not supported by this version of the C# compiler. + // lock (l) { } + Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "lock (l) { }").WithArguments("System.Threading.Lock.EnterLockScope()", "Test").WithLocation(5, 9)); + } + + [Fact] + public void UseSiteError_Scope() + { + // namespace System.Threading + // { + // public class Lock + // { + // public Scope EnterLockScope() => throw null; + // + // [System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute("Test")] + // public ref struct Scope + // { + // public void Dispose() { } + // } + // } + // } + var ilSource = """ + .class public auto ansi sealed beforefieldinit System.Threading.Lock extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + + .method public hidebysig instance class System.Threading.Lock/Scope EnterLockScope () cil managed + { + .maxstack 8 + ldnull + throw + } + + .class nested public sequential ansi sealed beforefieldinit Scope extends System.ValueType + { + .custom instance void System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + + .custom instance void System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = ( + 01 00 04 54 65 73 74 00 00 + ) + + .pack 0 + .size 1 + + .method public hidebysig instance void Dispose () cil managed + { + .maxstack 8 + ret + } + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor(string s) cil managed + { + .maxstack 8 + ret + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsByRefLikeAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + """; + var source = """ + class C + { + void M(System.Threading.Lock l) + { + lock (l) { } + } + } + """; + CreateCompilationWithIL(source, ilSource).VerifyEmitDiagnostics( + // (5,9): error CS9041: 'Lock.Scope' requires compiler feature 'Test', which is not supported by this version of the C# compiler. + // lock (l) { } + Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "lock (l) { }").WithArguments("System.Threading.Lock.Scope", "Test").WithLocation(5, 9), + // (5,9): error CS9041: 'Lock.Scope' requires compiler feature 'Test', which is not supported by this version of the C# compiler. + // lock (l) { } + Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "lock (l) { }").WithArguments("System.Threading.Lock.Scope", "Test").WithLocation(5, 9)); + } + + [Fact] + public void UseSiteError_Dispose() + { + // namespace System.Threading + // { + // public class Lock + // { + // public Scope EnterLockScope() => throw null; + // + // public ref struct Scope + // { + // [System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute("Test")] + // public void Dispose() { } + // } + // } + // } + var ilSource = """ + .class public auto ansi sealed beforefieldinit System.Threading.Lock extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + + .method public hidebysig instance class System.Threading.Lock/Scope EnterLockScope () cil managed + { + .maxstack 8 + ldnull + throw + } + + .class nested public sequential ansi sealed beforefieldinit Scope extends System.ValueType + { + .custom instance void System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + + .pack 0 + .size 1 + + .method public hidebysig instance void Dispose () cil managed + { + .custom instance void System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = ( + 01 00 04 54 65 73 74 00 00 + ) + .maxstack 8 + ret + } + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor(string s) cil managed + { + .maxstack 8 + ret + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsByRefLikeAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + """; + var source = """ + class C + { + void M(System.Threading.Lock l) + { + lock (l) { } + } + } + """; + CreateCompilationWithIL(source, ilSource).VerifyEmitDiagnostics( + // (5,9): error CS9041: 'Lock.Scope.Dispose()' requires compiler feature 'Test', which is not supported by this version of the C# compiler. + // lock (l) { } + Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "lock (l) { }").WithArguments("System.Threading.Lock.Scope.Dispose()", "Test").WithLocation(5, 9)); + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index e477803077b41..2b2e460f70efc 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -639,6 +639,7 @@ public void AllWellKnownTypes() case WellKnownType.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute: case WellKnownType.System_Runtime_InteropServices_MemoryMarshal: case WellKnownType.System_Runtime_CompilerServices_Unsafe: + case WellKnownType.System_Threading_Lock: // Not yet in the platform. continue; case WellKnownType.Microsoft_CodeAnalysis_Runtime_Instrumentation: diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index d3c38f6908576..4f2975127af60 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -639,6 +639,9 @@ internal enum WellKnownMember System_Collections_Immutable_ImmutableArray_T__AsSpan, System_Collections_Generic_List_T__AddRange, + System_Threading_Lock__EnterLockScope, + System_Threading_Lock__Scope__Dispose, + Count, // Remember to update the AllWellKnownTypeMembers tests when making changes here diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 3365e9caf63e1..043800bd72bed 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -4443,6 +4443,20 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Collections_Generic_IEnumerable_T, 1, (byte)SignatureTypeCode.GenericTypeParameter, 0, + + // System_Threading_Lock__EnterLockScope + (byte)MemberFlags.Method, // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_Lock - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_Lock__Scope - WellKnownType.ExtSentinel), // Return Type + + // System_Threading_Lock__Scope__Dispose + (byte)MemberFlags.Method, // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_Lock__Scope - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type }; string[] allNames = new string[(int)WellKnownMember.Count] @@ -4992,6 +5006,8 @@ static WellKnownMembers() "CopyTo", // System_ReadOnlySpan_T__CopyTo_Span_T "AsSpan", // System_Collections_Immutable_ImmutableArray_T__AsSpan "AddRange", // System_Collections_Generic_List_T__AddRange + "EnterLockScope", // System_Threading_Lock__EnterLockScope + "Dispose", // System_Threading_Lock__Scope__Dispose }; s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames); diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 690a8b4feac8b..6c303a5cc0d28 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -337,6 +337,9 @@ internal enum WellKnownType System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute, System_Runtime_CompilerServices_Unsafe, + System_Threading_Lock, + System_Threading_Lock__Scope, + NextAvailable, // Remember to update the AllWellKnownTypes tests when making changes here } @@ -662,6 +665,9 @@ internal static class WellKnownTypes "System.MissingMethodException", "System.Runtime.CompilerServices.MetadataUpdateOriginalTypeAttribute", "System.Runtime.CompilerServices.Unsafe", + + "System.Threading.Lock", + "System.Threading.Lock+Scope", }; private static readonly Dictionary s_nameToTypeIdMap = new Dictionary((int)Count); diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 8700f144d3848..118ebf07330f5 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -563,7 +563,8 @@ End Namespace WellKnownType.System_Runtime_CompilerServices_Unsafe, WellKnownType.System_Runtime_CompilerServices_RequiresLocationAttribute, WellKnownType.System_Runtime_InteropServices_CollectionsMarshal, - WellKnownType.System_Runtime_InteropServices_ImmutableCollectionsMarshal + WellKnownType.System_Runtime_InteropServices_ImmutableCollectionsMarshal, + WellKnownType.System_Threading_Lock ' Not available on all platforms. Continue For Case WellKnownType.ExtSentinel @@ -644,7 +645,8 @@ End Namespace WellKnownType.System_Runtime_CompilerServices_Unsafe, WellKnownType.System_Runtime_CompilerServices_RequiresLocationAttribute, WellKnownType.System_Runtime_InteropServices_CollectionsMarshal, - WellKnownType.System_Runtime_InteropServices_ImmutableCollectionsMarshal + WellKnownType.System_Runtime_InteropServices_ImmutableCollectionsMarshal, + WellKnownType.System_Threading_Lock ' Not available on all platforms. Continue For Case WellKnownType.ExtSentinel