diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 0de1a60a39fba..88c32506faebe 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -3187,7 +3187,7 @@ private BoundExpression BindIsOperator(BinaryExpressionSyntax node, BindingDiagn } bool hasErrors = node.Right.HasErrors; - var convertedExpression = BindExpressionForPattern(operand.Type, node.Right, ref hasErrors, isPatternDiagnostics, out var constantValueOpt, out var wasExpression); + var convertedExpression = BindExpressionForPattern(operand.Type, node.Right, ref hasErrors, isPatternDiagnostics, out var constantValueOpt, out var wasExpression, out _); if (wasExpression) { hasErrors |= constantValueOpt is null; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index e4dc68b4cff54..7feebb8285e90 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -408,7 +408,7 @@ internal BoundPattern BindConstantPatternWithFallbackToTypePattern( BindingDiagnosticBag diagnostics) { ExpressionSyntax innerExpression = SkipParensAndNullSuppressions(expression, diagnostics, ref hasErrors); - var convertedExpression = BindExpressionOrTypeForPattern(inputType, innerExpression, ref hasErrors, diagnostics, out var constantValueOpt, out bool wasExpression); + var convertedExpression = BindExpressionOrTypeForPattern(inputType, innerExpression, ref hasErrors, diagnostics, out var constantValueOpt, out bool wasExpression, out Conversion patternConversion); if (wasExpression) { var convertedType = convertedExpression.Type ?? inputType; @@ -417,6 +417,12 @@ internal BoundPattern BindConstantPatternWithFallbackToTypePattern( convertedType = inputType; } + if (HasBlockedINumberConversion(patternConversion, inputType)) + { + // Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + diagnostics.Add(ErrorCode.ERR_CannotMatchOnINumberBase, node.Location, inputType); + } + return new BoundConstantPattern( node, convertedExpression, constantValueOpt ?? ConstantValue.Bad, inputType, convertedType, hasErrors || constantValueOpt is null); } @@ -431,6 +437,29 @@ internal BoundPattern BindConstantPatternWithFallbackToTypePattern( } } + private bool HasBlockedINumberConversion(Conversion patternConversion, TypeSymbol inputType) + { + // We want to block constant and relation patterns that have an input type constrained to or inherited from INumberBase, if we don't + // know how to represent the constant being matched against in the input type. For example, `1.0 is 1` will work when written inline, but + // will fail if the input type is `INumberBase`. We block this now so that we can make make it work as expected in the future without + // being a breaking change. + + if (patternConversion.IsIdentity || patternConversion.IsConstantExpression || patternConversion.IsNumeric) + { + return false; + } + + var iNumberBase = Compilation.GetWellKnownType(WellKnownType.System_Numerics_INumberBase_T); + + if (iNumberBase.IsErrorType()) + { + return false; + } + + var interfaces = inputType is TypeParameterSymbol typeParam ? typeParam.EffectiveInterfacesNoUseSiteDiagnostics : inputType.AllInterfacesNoUseSiteDiagnostics; + return interfaces.Any(static (i, iNumberBase) => i.OriginalDefinition.Equals(iNumberBase), iNumberBase); + } + private static ExpressionSyntax SkipParensAndNullSuppressions(ExpressionSyntax e, BindingDiagnosticBag diagnostics, ref bool hasErrors) { while (true) @@ -465,19 +494,21 @@ private BoundExpression BindExpressionOrTypeForPattern( ref bool hasErrors, BindingDiagnosticBag diagnostics, out ConstantValue? constantValueOpt, - out bool wasExpression) + out bool wasExpression, + out Conversion patternExpressionConversion) { constantValueOpt = null; BoundExpression expression = BindTypeOrRValue(patternExpression, diagnostics); wasExpression = expression.Kind != BoundKind.TypeExpression; if (wasExpression) { - return BindExpressionForPatternContinued(expression, inputType, patternExpression, ref hasErrors, diagnostics, out constantValueOpt); + return BindExpressionForPatternContinued(expression, inputType, patternExpression, ref hasErrors, diagnostics, out constantValueOpt, out patternExpressionConversion); } else { Debug.Assert(expression is { Kind: BoundKind.TypeExpression, Type: { } }); hasErrors |= CheckValidPatternType(patternExpression, inputType, expression.Type, diagnostics: diagnostics); + patternExpressionConversion = Conversion.NoConversion; return expression; } } @@ -491,13 +522,15 @@ private BoundExpression BindExpressionForPattern( ref bool hasErrors, BindingDiagnosticBag diagnostics, out ConstantValue? constantValueOpt, - out bool wasExpression) + out bool wasExpression, + out Conversion patternExpressionConversion) { constantValueOpt = null; var expression = BindExpression(patternExpression, diagnostics: diagnostics, invoked: false, indexed: false); expression = CheckValue(expression, BindValueKind.RValue, diagnostics); wasExpression = expression.Kind switch { BoundKind.BadExpression => false, BoundKind.TypeExpression => false, _ => true }; - return wasExpression ? BindExpressionForPatternContinued(expression, inputType, patternExpression, ref hasErrors, diagnostics, out constantValueOpt) : expression; + patternExpressionConversion = Conversion.NoConversion; + return wasExpression ? BindExpressionForPatternContinued(expression, inputType, patternExpression, ref hasErrors, diagnostics, out constantValueOpt, out patternExpressionConversion) : expression; } private BoundExpression BindExpressionForPatternContinued( @@ -506,10 +539,11 @@ private BoundExpression BindExpressionForPatternContinued( ExpressionSyntax patternExpression, ref bool hasErrors, BindingDiagnosticBag diagnostics, - out ConstantValue? constantValueOpt) + out ConstantValue? constantValueOpt, + out Conversion patternExpressionConversion) { BoundExpression convertedExpression = ConvertPatternExpression( - inputType, patternExpression, expression, out constantValueOpt, hasErrors, diagnostics); + inputType, patternExpression, expression, out constantValueOpt, hasErrors, diagnostics, out patternExpressionConversion); ConstantValueUtils.CheckLangVersionForConstantValue(convertedExpression, diagnostics); @@ -541,7 +575,8 @@ internal BoundExpression ConvertPatternExpression( BoundExpression expression, out ConstantValue? constantValue, bool hasErrors, - BindingDiagnosticBag diagnostics) + BindingDiagnosticBag diagnostics, + out Conversion patternExpressionConversion) { BoundExpression convertedExpression; @@ -577,16 +612,24 @@ internal BoundExpression ConvertPatternExpression( if (!hasErrors) { var requiredVersion = MessageID.IDS_FeatureRecursivePatterns.RequiredVersion(); - if (Compilation.LanguageVersion < requiredVersion && - !this.Conversions.ClassifyConversionFromExpression(expression, inputType, isChecked: CheckOverflowAtRuntime, ref useSiteInfo).IsImplicit) + patternExpressionConversion = this.Conversions.ClassifyConversionFromExpression(expression, inputType, isChecked: CheckOverflowAtRuntime, ref useSiteInfo); + if (Compilation.LanguageVersion < requiredVersion && !patternExpressionConversion.IsImplicit) { diagnostics.Add(ErrorCode.ERR_ConstantPatternVsOpenType, expression.Syntax.Location, inputType, expression.Display, new CSharpRequiredLanguageVersion(requiredVersion)); } } + else + { + patternExpressionConversion = Conversion.NoConversion; + } diagnostics.Add(node, useSiteInfo); } + else + { + patternExpressionConversion = Conversion.NoConversion; + } } else { @@ -613,13 +656,16 @@ internal BoundExpression ConvertPatternExpression( { diagnostics.Add(ErrorCode.ERR_PatternSpanCharCannotBeStringNull, convertedExpression.Syntax.Location, inputType); } + + patternExpressionConversion = Conversion.NoConversion; + return convertedExpression; } // This will allow user-defined conversions, even though they're not permitted here. This is acceptable // because the result of a user-defined conversion does not have a ConstantValue. A constant pattern // requires a constant value so we'll report a diagnostic to that effect later. - convertedExpression = GenerateConversionForAssignment(inputType, expression, diagnostics); + convertedExpression = GenerateConversionForAssignment(inputType, expression, diagnostics, out patternExpressionConversion); if (convertedExpression.Kind == BoundKind.Conversion) { @@ -1578,7 +1624,7 @@ private BoundPattern BindRelationalPattern( bool hasErrors, BindingDiagnosticBag diagnostics) { - BoundExpression value = BindExpressionForPattern(inputType, node.Expression, ref hasErrors, diagnostics, out var constantValueOpt, out _); + BoundExpression value = BindExpressionForPattern(inputType, node.Expression, ref hasErrors, diagnostics, out var constantValueOpt, out _, out Conversion patternConversion); ExpressionSyntax innerExpression = SkipParensAndNullSuppressions(node.Expression, diagnostics, ref hasErrors); RoslynDebug.Assert(value.Type is { }); BinaryOperatorKind operation = tokenKindToBinaryOperatorKind(node.OperatorToken.Kind()); @@ -1616,6 +1662,13 @@ private BoundPattern BindRelationalPattern( constantValueOpt = ConstantValue.Bad; } + if (!hasErrors && HasBlockedINumberConversion(patternConversion, inputType)) + { + // Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + diagnostics.Add(ErrorCode.ERR_CannotMatchOnINumberBase, node.Location, inputType); + hasErrors = true; + } + return new BoundRelationalPattern(node, operation | opType, value, constantValueOpt, inputType, value.Type, hasErrors); static BinaryOperatorKind tokenKindToBinaryOperatorKind(SyntaxKind kind) => kind switch diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 4e0483caaf255..9af60ddf77de8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1870,6 +1870,9 @@ internal enum ConversionForAssignmentFlags } internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, BoundExpression expression, BindingDiagnosticBag diagnostics, ConversionForAssignmentFlags flags = ConversionForAssignmentFlags.None) + => GenerateConversionForAssignment(targetType, expression, diagnostics, out _, flags); + + internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, BoundExpression expression, BindingDiagnosticBag diagnostics, out Conversion conversion, ConversionForAssignmentFlags flags = ConversionForAssignmentFlags.None) { Debug.Assert((object)targetType != null); Debug.Assert(expression != null); @@ -1889,7 +1892,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - var conversion = (flags & ConversionForAssignmentFlags.IncrementAssignment) == 0 ? + conversion = (flags & ConversionForAssignmentFlags.IncrementAssignment) == 0 ? this.Conversions.ClassifyConversionFromExpression(expression, targetType, isChecked: CheckOverflowAtRuntime, ref useSiteInfo) : this.Conversions.ClassifyConversionFromType(expression.Type, targetType, isChecked: CheckOverflowAtRuntime, ref useSiteInfo); diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs index 6e8fbb7f207e6..5d193953c36c0 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs @@ -268,7 +268,7 @@ protected BoundExpression ConvertCaseExpression(CSharpSyntaxNode node, BoundExpr caseExpression = CreateConversion(caseExpression, conversion, SwitchGoverningType, diagnostics); } - return ConvertPatternExpression(SwitchGoverningType, node, caseExpression, out constantValueOpt, hasErrors, diagnostics); + return ConvertPatternExpression(SwitchGoverningType, node, caseExpression, out constantValueOpt, hasErrors, diagnostics, out _); } private static readonly object s_nullKey = new object(); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 69bfcdca2d78c..60677940ef2e6 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7178,4 +7178,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ file types + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 472d2bff37f42..4f64473d9442a 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2106,6 +2106,7 @@ internal enum ErrorCode ERR_GlobalUsingStaticFileType = 9055, ERR_FileTypeNameDisallowed = 9056, ERR_FeatureNotAvailableInVersion11 = 9058, + ERR_CannotMatchOnINumberBase = 9059, #endregion diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 7e58ff02580a0..e855eda53a3a4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -257,6 +257,11 @@ Nedal se odvodit typ delegáta. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. Konvence volání managed se nedá kombinovat se specifikátory konvence nespravovaného volání. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 3affc94baac3d..3a523a57e6abd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -257,6 +257,11 @@ Der Delegattyp konnte nicht abgeleitet werden. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. Die Aufrufkonvention "managed" kann nicht mit Spezifizierern für nicht verwaltete Aufrufkonventionen kombiniert werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 8cac02df3fc3e..027626323175d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -257,6 +257,11 @@ El tipo de delegado no se puede deducir. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. La convención de llamada "managed" no se puede combinar con especificadores de convención de llamada no administrados. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index e44ebe336ed86..7c347d0379569 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -257,6 +257,11 @@ Impossible de déduire le type délégué. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. Impossible d'associer la convention d'appel 'managed' à des spécificateurs de convention d'appel non managés. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index e339116673a1e..79d33f0738afb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -257,6 +257,11 @@ Non è possibile dedurre il tipo di delegato. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. Non è possibile combinare la convenzione di chiamata 'managed' con identificatori di convenzione di chiamata non gestita. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index e109544a8f9cb..bda602df9cc66 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -257,6 +257,11 @@ デリゲート型を推論できませんでした。 + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. 'マネージド' 呼び出し規則をアンマネージド呼び出し規則指定子と組み合わせることはできません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index adb4fec56b30e..bc121b9f10ab5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -257,6 +257,11 @@ 대리자 형식을 유추할 수 없습니다. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. '관리되는' 호출 규칙은 관리되지 않는 호출 규칙 지정자와 함께 사용할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 36ed6f0d45577..1d1f9569c54b3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -257,6 +257,11 @@ Nie można wywnioskować typu delegowania. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. Konwencji wywoływania „managed” nie można łączyć z niezarządzanymi specyfikatorami konwencji wywoływania. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 702e8b212618e..16599a8f58b6f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -257,6 +257,11 @@ O tipo de representante não pôde ser inferido. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. A convenção de chamada 'managed' não pode ser combinada com especificadores de convenção de chamada não gerenciados. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index c2af4d66a1c1b..fadd40d4f9c5b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -257,6 +257,11 @@ Не удалось вывести тип делегата. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. Соглашение о вызовах "managed" невозможно использовать вместе с спецификаторами неуправляемых соглашений о вызовах. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 4d9f80bbddfef..8f21c4c871fba 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -257,6 +257,11 @@ Temsilci türü çıkarsanamadı. + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. 'managed' çağırma kuralı, yönetilmeyen çağırma kuralı tanımlayıcılarla birleştirilemez. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 5117c46b5cb7f..69b8b0035cbcf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -257,6 +257,11 @@ 无法推断委托类型。 + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. "managed" 调用约定不能与非托管调用约定说明符一起使用。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 7953331173964..6d6ed37066fb5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -257,6 +257,11 @@ 無法推斷委派類型。 + + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + + 'managed' calling convention cannot be combined with unmanaged calling convention specifiers. 'managed' 呼叫慣例不得與未受控的呼叫慣例指定名稱並用。 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs index 2eb874bb6b89b..e6c17ed814dd6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs @@ -1949,5 +1949,296 @@ public void Repro55184() Diagnostic(ErrorCode.ERR_NoSuchMember, "Error").WithArguments("int", "Error").WithLocation(7, 19) ); } + + private const string INumberBaseDefinition = """ + namespace System.Numerics; + public interface INumberBase where T : INumberBase {} + """; + + [Fact] + public void ForbiddenOnTypeParametersConstrainedToINumberBase_01() + { + var source = """ + using System.Numerics; + + void M(T t) where T : INumberBase + { + int o = t switch + { + 1 => 1, // 1 + > 1 => 2, // 2 + int => 3, // OK + [] => 4, // 3 + (_) => 5 // 4 + { } => 6, // OK + var x => 7, // 5, subsumed by 6 + _ => 8 // Ok + }; + } + """; + + var comp = CreateCompilation(new[] { source, INumberBaseDefinition }); + comp.VerifyDiagnostics( + // (3,6): warning CS8321: The local function 'M' is declared but never used + // void M(T t) where T : INumberBase + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "M").WithArguments("M").WithLocation(3, 6), + // (7,9): error CS9059: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // 1 => 1, // 1 + Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "1").WithArguments("T").WithLocation(7, 9), + // (8,9): error CS9059: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // > 1 => 2, // 2 + Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "> 1").WithArguments("T").WithLocation(8, 9), + // (10,9): error CS8985: List patterns may not be used for a value of type 'T'. No suitable 'Length' or 'Count' property was found. + // [] => 4, // 3 + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("T").WithLocation(10, 9), + // (10,9): error CS0518: Predefined type 'System.Index' is not defined or imported + // [] => 4, // 3 + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(10, 9), + // (10,9): error CS0021: Cannot apply indexing with [] to an expression of type 'T' + // [] => 4, // 3 + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[]").WithArguments("T").WithLocation(10, 9), + // (11,17): error CS1003: Syntax error, ',' expected + // (_) => 5 // 4 + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(11, 17) + ); + } + + [Fact] + public void ForbiddenOnTypeParametersConstrainedToINumberBase_02() + { + var source = """ + using System.Numerics; + + void M(T t) where T : struct, INumberBase + { + int o = t switch + { + 1 => 1, // 1 + > 1 => 2, // 2 + int => 3, // OK + [] => 4, // 3 + (_) => 5 // 4 + { } => 6, // OK + var x => 7, // 5, subsumed by 6 + _ => 8 // Ok + }; + } + """; + + var comp = CreateCompilation(new[] { source, INumberBaseDefinition }); + comp.VerifyDiagnostics( + // (3,6): warning CS8321: The local function 'M' is declared but never used + // void M(T t) where T : INumberBase + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "M").WithArguments("M").WithLocation(3, 6), + // (7,9): error CS9059: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // 1 => 1, // 1 + Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "1").WithArguments("T").WithLocation(7, 9), + // (8,9): error CS9059: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // > 1 => 2, // 2 + Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "> 1").WithArguments("T").WithLocation(8, 9), + // (10,9): error CS8985: List patterns may not be used for a value of type 'T'. No suitable 'Length' or 'Count' property was found. + // [] => 4, // 3 + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("T").WithLocation(10, 9), + // (10,9): error CS0518: Predefined type 'System.Index' is not defined or imported + // [] => 4, // 3 + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(10, 9), + // (10,9): error CS0021: Cannot apply indexing with [] to an expression of type 'T' + // [] => 4, // 3 + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[]").WithArguments("T").WithLocation(10, 9), + // (11,17): error CS1003: Syntax error, ',' expected + // (_) => 5 // 4 + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(11, 17) + ); + } + + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("interface")] + public void ForbiddenOnTypeParametersInheritingFromINumberBase(string type) + { + var source = $$""" + using System.Numerics; + + C c = null; + int o = c switch + { + 1 => 1, // 1 + > 1 => 2, // 2 + int => 3, // OK + [] => 4, // 3 + (_) => 5 // 4 + { } => 6, // OK + var x => 7, // 5, subsumed by 6 + _ => 8 // Ok + }; + + {{type}} C : INumberBase + { + } + """; + + var comp = CreateCompilation(new[] { source, INumberBaseDefinition }); + comp.VerifyDiagnostics( + // (6,5): error CS0029: Cannot implicitly convert type 'int' to 'C' + // 1 => 1, // 1 + Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "C").WithLocation(6, 5), + // (6,5): error CS9059: Cannot use a numeric constant or relational pattern on 'C' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // 1 => 1, // 1 + Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "1").WithArguments("C").WithLocation(6, 5), + // (7,5): error CS8781: Relational patterns may not be used for a value of type 'C'. + // > 1 => 2, // 2 + Diagnostic(ErrorCode.ERR_UnsupportedTypeForRelationalPattern, "> 1").WithArguments("C").WithLocation(7, 5), + // (7,7): error CS0029: Cannot implicitly convert type 'int' to 'C' + // > 1 => 2, // 2 + Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "C").WithLocation(7, 7), + // (8,5): error CS8121: An expression of type 'C' cannot be handled by a pattern of type 'int'. + // int => 3, // OK + Diagnostic(ErrorCode.ERR_PatternWrongType, "int").WithArguments("C", "int").WithLocation(8, 5), + // (9,5): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // [] => 4, // 3 + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("C").WithLocation(9, 5), + // (9,5): error CS0518: Predefined type 'System.Index' is not defined or imported + // [] => 4, // 3 + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(9, 5), + // (9,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' + // [] => 4, // 3 + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[]").WithArguments("C").WithLocation(9, 5), + // (10,13): error CS1003: Syntax error, ',' expected + // (_) => 5 // 4 + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(10, 13) + ); + } + + private const string INumberBaseBCL = """ + namespace System + { + using System.Numerics; + + public class Object {} + public class Void {} + public class ValueType {} + public class String {} + public class Enum {} + public struct Nullable where T : struct {} + public struct Byte : INumberBase {} + public struct SByte : INumberBase {} + public struct Int16 : INumberBase {} + public struct Char : INumberBase {} + public struct UInt16 : INumberBase {} + public struct Int32 : INumberBase {} + public struct UInt32 : INumberBase {} + public struct Int64 : INumberBase {} + public struct UInt64 : INumberBase {} + public struct Single : INumberBase {} + public struct Double : INumberBase {} + public struct Decimal : INumberBase {} + public struct IntPtr : INumberBase {} + public struct UIntPtr : INumberBase {} + } + """; + + [Theory] + [InlineData("byte")] + [InlineData("sbyte")] + [InlineData("short")] + [InlineData("ushort")] + [InlineData("int")] + [InlineData("uint")] + [InlineData("nint")] + [InlineData("nuint")] + [InlineData("long")] + [InlineData("ulong")] + [InlineData("float")] + [InlineData("double")] + [InlineData("decimal")] + public void MatchingOnConstantConversionsWithINumberBaseIsAllowed(string inputType) + { + var source = $$""" + {{inputType}} i = 1; + _ = i switch + { + 1 => 1, + > 1 => 2, + _ => 3 + }; + """; + + var comp = CreateEmptyCompilation(new[] { source, INumberBaseBCL, INumberBaseDefinition }); + comp.VerifyDiagnostics(); + } + + [Theory] + [InlineData("byte")] + [InlineData("sbyte")] + [InlineData("short")] + [InlineData("ushort")] + [InlineData("int")] + [InlineData("uint")] + [InlineData("nint")] + [InlineData("nuint")] + [InlineData("long")] + [InlineData("ulong")] + [InlineData("float")] + [InlineData("double")] + [InlineData("decimal")] + public void MatchingOnConstantConversionsWithINumberBaseIsAllowed_Nullable(string inputType) + { + var source = $$""" + {{inputType}}? i = 1; + _ = i switch + { + 1 => 1, + > 1 => 2, + _ => 3 + }; + """; + + var comp = CreateEmptyCompilation(new[] { source, INumberBaseBCL, INumberBaseDefinition }); + comp.VerifyDiagnostics(); + } + + [Theory] + [InlineData("byte")] + [InlineData("sbyte")] + [InlineData("short")] + [InlineData("ushort")] + [InlineData("int")] + [InlineData("uint")] + [InlineData("nint")] + [InlineData("nuint")] + [InlineData("long")] + [InlineData("ulong")] + [InlineData("float")] + [InlineData("double")] + [InlineData("decimal")] + public void MatchingOnConstantConversionsWithINumberBaseIsAllowed_TypePatternToINumberBaseT(string inputType) + { + var source = $$""" + using System.Numerics; + {{inputType}}? i = 1; + _ = ((INumberBase<{{inputType}}>)i) switch + { + 1 => 1, + > 1 => 2, + _ => 3 + }; + """; + + var comp = CreateEmptyCompilation(new[] { source, INumberBaseBCL, INumberBaseDefinition }); + // These errors are not the "Cannot use a numeric constant or relational pattern on 'C' because it inherits from or extends 'INumberBase'" errors, + // they are standard inapplicability errors for the interface type itself. + comp.VerifyDiagnostics( + // (5,5): error CS0029: Cannot implicitly convert type 'int' to 'System.Numerics.INumberBase' + // 1 => 1, + Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "System.Numerics.INumberBase").WithLocation(5, 5), + // (6,5): error CS8781: Relational patterns may not be used for a value of type 'System.Numerics.INumberBase'. + // > 1 => 2, + Diagnostic(ErrorCode.ERR_UnsupportedTypeForRelationalPattern, "> 1").WithArguments("System.Numerics.INumberBase").WithLocation(6, 5), + // (6,7): error CS0029: Cannot implicitly convert type 'int' to 'System.Numerics.INumberBase' + // > 1 => 2, + Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "System.Numerics.INumberBase").WithLocation(6, 7) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index e61298854268a..6630eab0f4787 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -617,6 +617,7 @@ public void AllWellKnownTypes() case WellKnownType.System_Runtime_CompilerServices_LifetimeAnnotationAttribute: case WellKnownType.System_MemoryExtensions: case WellKnownType.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute: + case WellKnownType.System_Numerics_INumberBase_T: // Not yet in the platform. continue; case WellKnownType.Microsoft_CodeAnalysis_Runtime_Instrumentation: diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 41bf3dc7e746d..36948cab0f4ca 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -326,6 +326,8 @@ internal enum WellKnownType System_MissingMethodException, + System_Numerics_INumberBase_T, + NextAvailable, // Remember to update the AllWellKnownTypes tests when making changes here } @@ -641,6 +643,8 @@ internal static class WellKnownTypes "System.MemoryExtensions", "System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute", "System.MissingMethodException", + + "System.Numerics.INumberBase`1", }; 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 77ead726d1968..3fb08c1b09ad8 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -551,8 +551,9 @@ End Namespace WellKnownType.System_Diagnostics_CodeAnalysis_SetsRequiredMembersAttribute, WellKnownType.System_MemoryExtensions, WellKnownType.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute, - WellKnownType.System_Runtime_CompilerServices_LifetimeAnnotationAttribute, - WellKnownType.System_MemoryExtensions + WellKnownType.System_Runtime_CompilerServices_LifetimeAnnotationAttribute, + WellKnownType.System_MemoryExtensions, + WellKnownType.System_Numerics_INumberBase_T ' Not available on all platforms. Continue For Case WellKnownType.ExtSentinel @@ -623,7 +624,8 @@ End Namespace WellKnownType.System_Diagnostics_CodeAnalysis_SetsRequiredMembersAttribute, WellKnownType.System_MemoryExtensions, WellKnownType.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute, - WellKnownType.System_Runtime_CompilerServices_LifetimeAnnotationAttribute + WellKnownType.System_Runtime_CompilerServices_LifetimeAnnotationAttribute, + WellKnownType.System_Numerics_INumberBase_T ' Not available on all platforms. Continue For Case WellKnownType.ExtSentinel