diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index abda6b2ac0786..0981e775649e0 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -28,6 +28,7 @@ efforts behind them. | [Params-collections](https://github.com/dotnet/csharplang/issues/7700) | main | [Merged to 17.10p3](https://github.com/dotnet/roslyn/issues/71137) | [AlekseyTs](https://github.com/AlekseyTs) | [RikkiGibson](https://github.com/RikkiGibson), [333fred](https://github.com/333fred) | [akhera99](https://github.com/akhera99) (needs IDE fixer) | [MadsTorgersen](https://github.com/MadsTorgersen), [AlekseyTs](https://github.com/AlekseyTs) | | [Ref/unsafe in iterators/async](https://github.com/dotnet/csharplang/blob/main/proposals/ref-unsafe-in-iterators-async.md) | [RefInAsync](https://github.com/dotnet/roslyn/tree/features/RefInAsync) | [Merged into 17.11p2](https://github.com/dotnet/roslyn/issues/72662) | [jjonescz](https://github.com/jjonescz) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | (no IDE impact) | | | [`allows ref struct` constraint](https://github.com/dotnet/csharplang/issues/7608) | [RefStructInterfaces](https://github.com/dotnet/roslyn/tree/features/RefStructInterfaces) | [Merged into 17.11p2](https://github.com/dotnet/roslyn/issues/72124) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | [ToddGrun](https://github.com/ToddGrun) | [agocke](https://github.com/agocke), [jaredpar](https://github.com/jaredpar) | +| [Overload Resolution Priority](https://github.com/dotnet/csharplang/issues/7706) | main | [Merged to 17.12p1](https://github.com/dotnet/roslyn/issues/74131) | [333fred](https://github.com/333fred) | [jcouv](https://github.com/jcouv), [cston](https://github.com/cston) | Not yet assigned | [333fred](https://github.com/333fred) | | [Partial properties](https://github.com/dotnet/csharplang/issues/6420) | [partial-properties](https://github.com/dotnet/roslyn/tree/features/partial-properties) | [Merged into 17.11p3](https://github.com/dotnet/roslyn/issues/73090) | [RikkiGibson](https://github.com/RikkiGibson) | [jcouv](https://github.com/jcouv), [333fred](https://github.com/333fred) | [Cosifne](https://github.com/Cosifne) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | # C# 12.0 diff --git a/docs/contributing/Compiler Test Plan.md b/docs/contributing/Compiler Test Plan.md index 9d555eb18825d..f30047e69b4a5 100644 --- a/docs/contributing/Compiler Test Plan.md +++ b/docs/contributing/Compiler Test Plan.md @@ -85,6 +85,7 @@ This document provides guidance for thinking about language interactions and tes - Conversions (boxing/unboxing) - Nullable (wrapping, unwrapping) - Overload resolution, override/hide/implement (OHI) + - OverloadResolutionPriorityAttribute - Inheritance (virtual, override, abstract, new) - Anonymous types - Tuple types and literals (elements with explicit or inferred names, long tuples), tuple equality diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index cfcec3c172c3e..5ce1a7173d21d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -246,7 +246,6 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a diagnostics, attributedMember: attributedMember); boundConstructorArguments = analyzedArguments.ConstructorArguments.Arguments.ToImmutable(); - attributeArgumentBinder.ReportDiagnosticsIfObsolete(diagnostics, attributeConstructor, node, hasBaseReceiver: false); if (attributeConstructor.Parameters.Any(static p => p.RefKind is RefKind.In or RefKind.RefReadOnlyParameter)) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index d7ca3622909d8..7b600508a1c15 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -6543,7 +6543,7 @@ protected BoundExpression BindClassCreationExpression( CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); OverloadResolutionResult overloadResolutionResult = OverloadResolutionResult.GetInstance(); ImmutableArray accessibleConstructors = GetAccessibleConstructorsForOverloadResolution(type, ref useSiteInfo); - this.OverloadResolution.ObjectCreationOverloadResolution(accessibleConstructors, analyzedArguments, overloadResolutionResult, dynamicResolution: true, ref useSiteInfo); + this.OverloadResolution.ObjectCreationOverloadResolution(accessibleConstructors, analyzedArguments, overloadResolutionResult, dynamicResolution: true, isEarlyAttributeBinding: IsEarlyAttributeBinder, ref useSiteInfo); if (overloadResolutionResult.HasAnyApplicableMember) { @@ -7014,7 +7014,7 @@ internal bool TryPerformConstructorOverloadResolution( if (candidateConstructors.Any()) { // We have at least one accessible candidate constructor, perform overload resolution with accessible candidateConstructors. - this.OverloadResolution.ObjectCreationOverloadResolution(candidateConstructors, analyzedArguments, result, dynamicResolution: false, ref useSiteInfo); + this.OverloadResolution.ObjectCreationOverloadResolution(candidateConstructors, analyzedArguments, result, dynamicResolution: false, isEarlyAttributeBinding: IsEarlyAttributeBinder, ref useSiteInfo); if (result.Succeeded) { @@ -7028,7 +7028,7 @@ internal bool TryPerformConstructorOverloadResolution( // We might have a best match constructor which is inaccessible. // Try overload resolution with all instance constructors to generate correct diagnostics and semantic info for this case. OverloadResolutionResult inaccessibleResult = OverloadResolutionResult.GetInstance(); - this.OverloadResolution.ObjectCreationOverloadResolution(allInstanceConstructors, analyzedArguments, inaccessibleResult, dynamicResolution: false, ref useSiteInfo); + this.OverloadResolution.ObjectCreationOverloadResolution(allInstanceConstructors, analyzedArguments, inaccessibleResult, dynamicResolution: false, isEarlyAttributeBinding: IsEarlyAttributeBinder, ref useSiteInfo); if (inaccessibleResult.Succeeded) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs index 46ded8b36bc3f..d96fd06e7a0d4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs @@ -5,12 +5,13 @@ #nullable disable using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { [SuppressMessage("Performance", "CA1067", Justification = "Equality not actually implemented")] - internal readonly struct BinaryOperatorAnalysisResult + internal readonly struct BinaryOperatorAnalysisResult : IMemberResolutionResultWithPriority { public readonly Conversion LeftConversion; public readonly Conversion RightConversion; @@ -35,6 +36,8 @@ public bool HasValue get { return this.Kind != OperatorAnalysisResultKind.Undefined; } } + MethodSymbol IMemberResolutionResultWithPriority.MemberWithPriority => Signature.Method; + public override bool Equals(object obj) { // implement if needed diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs index ab9b9436f10ae..cc17aafebb262 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs @@ -1069,13 +1069,15 @@ private void BinaryOperatorOverloadResolution( return; } + var candidates = result.Results; + RemoveLowerPriorityMembers(candidates); + // SPEC: Otherwise, the best function member is the one function member that is better than all other function // SPEC: members with respect to the given argument list, provided that each function member is compared to all // SPEC: other function members using the rules in 7.5.3.2. If there is not exactly one function member that is // SPEC: better than all other function members, then the function member invocation is ambiguous and a binding-time // SPEC: error occurs. - var candidates = result.Results; // Try to find a single best candidate int bestIndex = GetTheBestCandidateIndex(left, right, candidates, ref useSiteInfo); if (bestIndex != -1) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs index 12e58c85a50e7..46773fa51a037 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs @@ -5,12 +5,10 @@ #nullable disable using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp { - internal readonly struct UnaryOperatorAnalysisResult + internal readonly struct UnaryOperatorAnalysisResult : IMemberResolutionResultWithPriority { public readonly UnaryOperatorSignature Signature; public readonly Conversion Conversion; @@ -33,6 +31,8 @@ public bool HasValue get { return this.Kind != OperatorAnalysisResultKind.Undefined; } } + MethodSymbol IMemberResolutionResultWithPriority.MemberWithPriority => Signature.Method; + public static UnaryOperatorAnalysisResult Applicable(UnaryOperatorSignature signature, Conversion conversion) { return new UnaryOperatorAnalysisResult(OperatorAnalysisResultKind.Applicable, signature, conversion); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs index 1d7e91ec1f7e0..de3c0a97d3a74 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs @@ -74,13 +74,15 @@ private void UnaryOperatorOverloadResolution( return; } + var candidates = result.Results; + RemoveLowerPriorityMembers(candidates); + // SPEC: Otherwise, the best function member is the one function member that is better than all other function // SPEC: members with respect to the given argument list, provided that each function member is compared to all // SPEC: other function members using the rules in 7.5.3.2. If there is not exactly one function member that is // SPEC: better than all other function members, then the function member invocation is ambiguous and a binding-time // SPEC: error occurs. - var candidates = result.Results; // Try to find a single best candidate int bestIndex = GetTheBestCandidateIndex(operand, candidates, ref useSiteInfo); if (bestIndex != -1) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs new file mode 100644 index 0000000000000..747c26b2c5228 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs @@ -0,0 +1,14 @@ +// 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. + +namespace Microsoft.CodeAnalysis.CSharp; + +/// +/// Abstraction for use in , +/// to allow it to work generically with all member resolution types (method, unary operator, binary operator). +/// +internal interface IMemberResolutionResultWithPriority where TMember : Symbol +{ + TMember? MemberWithPriority { get; } +} diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberResolutionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberResolutionResult.cs index b719b9604f3ef..9b4f43c99c790 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberResolutionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberResolutionResult.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Symbols; namespace Microsoft.CodeAnalysis.CSharp { @@ -11,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// Represents the results of overload resolution for a single member. /// [SuppressMessage("Performance", "CA1067", Justification = "Equality not actually implemented")] - internal readonly struct MemberResolutionResult where TMember : Symbol + internal readonly struct MemberResolutionResult : IMemberResolutionResultWithPriority where TMember : Symbol { private readonly TMember _member; private readonly TMember _leastOverriddenMember; @@ -121,6 +122,8 @@ internal MemberAnalysisResult Result get { return _result; } } + TMember IMemberResolutionResultWithPriority.MemberWithPriority => LeastOverriddenMember; + public override bool Equals(object? obj) { throw new NotSupportedException(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index b25e5c915b670..81a03f32b7847 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -98,20 +98,20 @@ private static bool SingleValidResult(ArrayBuilder constructors, AnalyzedArguments arguments, OverloadResolutionResult result, bool dynamicResolution, ref CompoundUseSiteInfo useSiteInfo) + public void ObjectCreationOverloadResolution(ImmutableArray constructors, AnalyzedArguments arguments, OverloadResolutionResult result, bool dynamicResolution, bool isEarlyAttributeBinding, ref CompoundUseSiteInfo useSiteInfo) { Debug.Assert(!dynamicResolution || arguments.HasDynamicArgument); var results = result.ResultsBuilder; // First, attempt overload resolution not getting complete results. - PerformObjectCreationOverloadResolution(results, constructors, arguments, completeResults: false, dynamicResolution: dynamicResolution, ref useSiteInfo); + PerformObjectCreationOverloadResolution(results, constructors, arguments, completeResults: false, dynamicResolution: dynamicResolution, isEarlyAttributeBinding, ref useSiteInfo); if (!OverloadResolutionResultIsValid(results, arguments.HasDynamicArgument)) { // We didn't get a single good result. Get full results of overload resolution and return those. result.Clear(); - PerformObjectCreationOverloadResolution(results, constructors, arguments, completeResults: true, dynamicResolution: dynamicResolution, ref useSiteInfo); + PerformObjectCreationOverloadResolution(results, constructors, arguments, completeResults: true, dynamicResolution: dynamicResolution, isEarlyAttributeBinding, ref useSiteInfo); } } @@ -362,6 +362,8 @@ private void PerformMemberOverloadResolution( if ((options & Options.DynamicResolution) == 0) { + RemoveLowerPriorityMembers, TMember>(results); + // SPEC: The best method of the set of candidate methods is identified. If a single best method cannot be identified, // SPEC: the method invocation is ambiguous, and a binding-time error occurs. @@ -1604,6 +1606,7 @@ private void PerformObjectCreationOverloadResolution( AnalyzedArguments arguments, bool completeResults, bool dynamicResolution, + bool isEarlyAttributeBinding, ref CompoundUseSiteInfo useSiteInfo) { // SPEC: The instance constructor to invoke is determined using the overload resolution @@ -1621,6 +1624,16 @@ private void PerformObjectCreationOverloadResolution( if (!dynamicResolution) { + if (!isEarlyAttributeBinding) + { + // If we're still decoding early attributes, we can get into a cycle here where we attempt to decode early attributes, + // which causes overload resolution, which causes us to attempt to decode early attributes, etc. Concretely, this means + // that OverloadResolutionPriorityAttribute won't affect early bound attributes, so you can't use OverloadResolutionPriorityAttribute + // to adjust what constructor of OverloadResolutionPriorityAttribute is chosen. See `CycleOnOverloadResolutionPriorityConstructor_02` for + // an example. + RemoveLowerPriorityMembers, MethodSymbol>(results); + } + // The best method of the set of candidate methods is identified. If a single best // method cannot be identified, the method invocation is ambiguous, and a binding-time // error occurs. @@ -1699,6 +1712,81 @@ private int GetTheBestCandidateIndex(ArrayBuilder(ArrayBuilder results) + where TMemberResolution : IMemberResolutionResultWithPriority + where TMember : Symbol + { + // - Then, the reduced set of candidate members is grouped by declaring type. Within each group: + // - Candidate function members are ordered by *overload_resolution_priority*. + // - All members that have a lower *overload_resolution_priority* than the highest found within its declaring type group are removed. + // - The reduced groups are then recombined into the final set of applicable candidate function members. + + if (results.Count < 2) + { + // Can't prune anything unless there's at least 2 candidates + return; + } + + // Attempt to avoid any allocations by starting with a quick pass through all results and seeing if any have non-default priority. If so, we'll do the full sort and filter. + if (results.All(r => r.MemberWithPriority?.GetOverloadResolutionPriority() is null or 0)) + { + // All default, nothing to do + return; + } + + bool removedMembers = false; + var resultsByContainingType = PooledDictionary>.GetInstance(); + + foreach (var result in results) + { + if (result.MemberWithPriority is null) + { + // Can happen for things like built-in binary operators + continue; + } + + var containingType = result.MemberWithPriority.ContainingType; + if (resultsByContainingType.TryGetValue(containingType, out var previousResults)) + { + var previousPriority = previousResults.First().MemberWithPriority.GetOverloadResolutionPriority(); + var currentPriority = result.MemberWithPriority.GetOverloadResolutionPriority(); + + if (currentPriority > previousPriority) + { + removedMembers = true; + resultsByContainingType[containingType] = OneOrMany.Create(result); + } + else if (currentPriority == previousPriority) + { + resultsByContainingType[containingType] = previousResults.Add(result); + } + else + { + removedMembers = true; + Debug.Assert(previousResults.All(r => r.MemberWithPriority.GetOverloadResolutionPriority() == previousPriority)); + } + } + else + { + resultsByContainingType.Add(containingType, OneOrMany.Create(result)); + } + } + + if (!removedMembers) + { + // No changes, so we can just return + resultsByContainingType.Free(); + return; + } + + results.Clear(); + foreach (var (_, resultsForType) in resultsByContainingType) + { + results.AddRange(resultsForType); + } + resultsByContainingType.Free(); + } + private void RemoveWorseMembers(ArrayBuilder> results, AnalyzedArguments arguments, ref CompoundUseSiteInfo useSiteInfo) where TMember : Symbol { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 70487940501ab..8081e95ceed5b 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7977,6 +7977,15 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ allows ref struct constraint + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + overload resolution priority + Attribute 'System.Runtime.CompilerServices.InlineArray' cannot be applied to a record struct. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index ab2c9759618f1..350d7a829cf11 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2341,6 +2341,9 @@ internal enum ErrorCode ERR_InlineArrayAttributeOnRecord = 9259, ERR_FeatureNotAvailableInVersion13 = 9260, + ERR_CannotApplyOverloadResolutionPriorityToOverride = 9261, + ERR_CannotApplyOverloadResolutionPriorityToMember = 9262, + // Note: you will need to do the following after adding errors: // 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 6dfead062c43d..619d08c98c3e8 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2452,6 +2452,8 @@ or ErrorCode.ERR_PartialPropertyRequiredDifference or ErrorCode.INF_IdentifierConflictWithContextualKeyword or ErrorCode.ERR_InlineArrayAttributeOnRecord or ErrorCode.ERR_FeatureNotAvailableInVersion13 + or ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToOverride + or ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember => false, }; #pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index d077e445111bd..5b9b1cc929ad5 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -291,6 +291,7 @@ internal enum MessageID IDS_FeatureFieldAndValueKeywords = MessageBase + 12846, IDS_FeatureAllowsRefStructConstraint = MessageBase + 12847, + IDS_OverloadResolutionPriority = MessageBase + 12848, } // Message IDs may refer to strings that need to be localized. @@ -483,6 +484,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureRefUnsafeInIteratorAsync: case MessageID.IDS_FeatureAllowsRefStructConstraint: case MessageID.IDS_FeaturePartialProperties: + case MessageID.IDS_OverloadResolutionPriority: return LanguageVersion.CSharp13; // C# 12.0 features. diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs index aa243c9cf0172..cae0ccd0b2648 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs @@ -160,6 +160,8 @@ internal override ObsoleteAttributeData ObsoleteAttributeData get { return null; } } + internal override int? TryGetOverloadResolutionPriority() => null; + bool ISynthesizedMethodBodyImplementationSymbol.HasMethodBodyDependency { get { return _getter.HasMethodBodyDependency; } diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs index 7fa7f2c09c182..14b838693648f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs @@ -201,6 +201,8 @@ public FieldSymbol BackingField get { return _backingField; } } + internal override int? TryGetOverloadResolutionPriority() => null; + public override bool Equals(Symbol obj, TypeCompareKind compareKind) { if (obj == null) diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs index 7b8c0f3e80e88..4e36c871c7ab4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs @@ -294,5 +294,10 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil builderArgument = null; return false; } + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs index e11767c30a2a2..136f99adf5013 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs @@ -92,5 +92,7 @@ public ErrorPropertySymbol(Symbol containingSymbol, TypeSymbol type, string name public override ImmutableArray ExplicitInterfaceImplementations { get { return ImmutableArray.Empty; } } public override ImmutableArray RefCustomModifiers { get { return ImmutableArray.Empty; } } + + internal override int? TryGetOverloadResolutionPriority() => null; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs index 0972470685604..53cb47531de8b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs @@ -865,5 +865,10 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? bui builderArgument = null; return false; } + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index d8b41d831de6a..4dd568eb77069 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -76,7 +76,8 @@ private struct PackedFlags // w = IsSetsRequiredMembersPopulated. 1 bit. // x = IsUnscopedRef. 1 bit. // y = IsUnscopedRefPopulated. 1 bit. - // 2 bits remain for future purposes. + // z = OverloadResolutionPriorityPopulated. 1 bit. + // 1 bits remain for future purposes. private const int MethodKindOffset = 0; private const int MethodKindMask = 0x1F; @@ -106,6 +107,7 @@ private struct PackedFlags private const int HasSetsRequiredMembersPopulatedBit = 0x1 << 28; private const int IsUnscopedRefBit = 0x1 << 29; private const int IsUnscopedRefPopulatedBit = 0x1 << 30; + private const int OverloadResolutionPriorityPopulatedBit = 0x1 << 31; private int _bits; @@ -146,6 +148,7 @@ public MethodKind MethodKind public bool HasSetsRequiredMembersPopulated => (_bits & HasSetsRequiredMembersPopulatedBit) != 0; public bool IsUnscopedRef => (_bits & IsUnscopedRefBit) != 0; public bool IsUnscopedRefPopulated => (_bits & IsUnscopedRefPopulatedBit) != 0; + public bool IsOverloadResolutionPriorityPopulated => (_bits & OverloadResolutionPriorityPopulatedBit) != 0; #if DEBUG static PackedFlags() @@ -268,6 +271,11 @@ public bool InitializeIsUnscopedRef(bool value) return ThreadSafeFlagOperations.Set(ref _bits, bitsToSet); } + + public void SetIsOverloadResolutionPriorityPopulated() + { + ThreadSafeFlagOperations.Set(ref _bits, OverloadResolutionPriorityPopulatedBit); + } } /// @@ -287,6 +295,7 @@ private sealed class UncommonFields public ImmutableArray _lazyNotNullMembersWhenTrue; public ImmutableArray _lazyNotNullMembersWhenFalse; public MethodSymbol _lazyExplicitClassOverride; + public int _lazyOverloadResolutionPriority; } private UncommonFields CreateUncommonFields() @@ -1673,5 +1682,27 @@ internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgu builderArgument = _containingType.ContainingPEModule.TryDecodeAttributeWithTypeArgument(this.Handle, AttributeDescription.AsyncMethodBuilderAttribute); return builderArgument is not null; } + + internal override int? TryGetOverloadResolutionPriority() + { + if (!_packedFlags.IsOverloadResolutionPriorityPopulated) + { + if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority)) + { + Interlocked.CompareExchange(ref AccessUncommonFields()._lazyOverloadResolutionPriority, priority, 0); + } +#if DEBUG + else + { + // 0 is the default if nothing is present in metadata, and we don't care about preserving the difference between "not present" and "set to the default value". + Debug.Assert(_uncommonFields is null or { _lazyOverloadResolutionPriority: 0 }); + } +#endif + + _packedFlags.SetIsOverloadResolutionPriorityPopulated(); + } + + return _uncommonFields?._lazyOverloadResolutionPriority; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs index 9151ef8c181a6..9aca739230ce6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs @@ -61,6 +61,7 @@ private struct PackedFlags // d = Use site diagnostic flag. 1 bit // o = Obsolete flag. 1 bit // c = Custom attributes flag. 1 bit + // p = Overload resolution priority populated. 1 bit private const int IsSpecialNameFlag = 1 << 0; private const int IsRuntimeSpecialNameFlag = 1 << 1; private const int CallMethodsDirectlyFlag = 1 << 2; @@ -71,6 +72,7 @@ private struct PackedFlags private const int IsUseSiteDiagnosticPopulatedBit = 1 << 8; private const int IsObsoleteAttributePopulatedBit = 1 << 9; private const int IsCustomAttributesPopulatedBit = 1 << 10; + private const int IsOverloadResolutionPriorityPopulatedBit = 1 << 11; private int _bits; @@ -141,6 +143,13 @@ public void SetCustomAttributesPopulated() } public readonly bool IsCustomAttributesPopulated => (_bits & IsCustomAttributesPopulatedBit) != 0; + + public void SetOverloadResolutionPriorityPopulated() + { + ThreadSafeFlagOperations.Set(ref _bits, IsOverloadResolutionPriorityPopulatedBit); + } + + public readonly bool IsOverloadResolutionPriorityPopulated => (_bits & IsOverloadResolutionPriorityPopulatedBit) != 0; } private sealed class UncommonFields @@ -149,6 +158,7 @@ private sealed class UncommonFields public Tuple _lazyDocComment; public CachedUseSiteInfo _lazyCachedUseSiteInfo = CachedUseSiteInfo.Uninitialized; public ObsoleteAttributeData _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized; + public int _lazyOverloadResolutionPriority; } internal static PEPropertySymbol Create( @@ -1011,6 +1021,29 @@ internal override bool HasRuntimeSpecialName get { return null; } } + internal override int? TryGetOverloadResolutionPriority() + { + Debug.Assert(IsIndexer || IsIndexedProperty); + if (!_flags.IsOverloadResolutionPriorityPopulated) + { + if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority)) + { + Interlocked.CompareExchange(ref AccessUncommonFields()._lazyOverloadResolutionPriority, priority, 0); + } +#if DEBUG + else + { + // 0 is the default if nothing is present in metadata, and we don't care about preserving the difference between "not present" and "set to the default value". + Debug.Assert(_uncommonFields is null or { _lazyOverloadResolutionPriority: 0 }); + } +#endif + + _flags.SetOverloadResolutionPriorityPopulated(); + } + + return _uncommonFields?._lazyOverloadResolutionPriority; + } + private sealed class PEPropertySymbolWithCustomModifiers : PEPropertySymbol { private readonly ImmutableArray _refCustomModifiers; diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index 6fa407961b385..f75a1d8d2729d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -1186,6 +1186,24 @@ internal virtual void AddSynthesizedReturnTypeAttributes(PEModuleBuilder moduleB internal abstract bool IsNullableAnalysisEnabled(); + /// + /// Gets the resolution priority of this method, 0 if not set. + /// + /// + /// Do not call this method from early attribute binding, cycles will occur. + /// + internal int OverloadResolutionPriority => CanHaveOverloadResolutionPriority ? (TryGetOverloadResolutionPriority() ?? 0) : 0; + + internal abstract int? TryGetOverloadResolutionPriority(); + + internal bool CanHaveOverloadResolutionPriority => + MethodKind is MethodKind.Ordinary + or MethodKind.Constructor + or MethodKind.UserDefinedOperator + or MethodKind.ReducedExtension + or MethodKind.LocalFunction + && !IsOverride; + #region IMethodSymbolInternal bool IMethodSymbolInternal.HasDeclarativeSecurity => HasDeclarativeSecurity; diff --git a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs index 025f377e0225e..94d59417b4c61 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs @@ -332,6 +332,23 @@ public sealed override SymbolKind Kind } } + internal int OverloadResolutionPriority + { + get + { + if (!CanHaveOverloadResolutionPriority) + { + return 0; + } + + return TryGetOverloadResolutionPriority() ?? 0; + } + } + + internal abstract int? TryGetOverloadResolutionPriority(); + + internal bool CanHaveOverloadResolutionPriority => !IsOverride && !IsExplicitInterfaceImplementation && (IsIndexer || IsIndexedProperty); + /// /// Implements visitor pattern. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index d5f5bcbe61e54..0eeaca77a03d8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -608,6 +608,11 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil #nullable enable + internal override int? TryGetOverloadResolutionPriority() + { + return _reducedFrom.TryGetOverloadResolutionPriority(); + } + private sealed class ReducedExtensionMethodParameterSymbol : WrappedParameterSymbol { private readonly ReducedExtensionMethodSymbol _containingMethod; diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs index 01a41fc44a014..6e07b286192b3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs @@ -184,6 +184,8 @@ internal override bool IsMetadataFinal internal sealed override bool UseUpdatedEscapeRules => true; + internal sealed override int? TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); + #endregion } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs index 12f55640560b6..d2e426f22923f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs @@ -103,6 +103,8 @@ public SignatureOnlyPropertySymbol( public override bool IsIndexer { get { throw ExceptionUtilities.Unreachable(); } } + internal override int? TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); + #endregion Not used by PropertySignatureComparer } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs index ed4d7403220ed..4cc4a464d7977 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs @@ -201,6 +201,11 @@ internal sealed override System.AttributeTargets GetAttributeTarget() return System.AttributeTargets.Delegate; } + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } + private sealed class Constructor : SourceDelegateMethodSymbol { internal Constructor( diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs index 4140bb68ca6df..0d41e34db2617 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs @@ -176,5 +176,10 @@ internal override bool GenerateDebugInfo { get { return true; } } + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs index 65ed2000d0c95..bde10d904f2eb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs @@ -228,5 +228,10 @@ protected string GetOverriddenAccessorName(SourceEventSymbol @event, bool isAdde return null; } + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 70a019b8eb217..64791d2be8f46 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -369,6 +369,28 @@ internal override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAt // diagnostics that might later get thrown away as possible when binding method calls. return (null, null); } + else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.OverloadResolutionPriorityAttribute)) + { + if (!CanHaveOverloadResolutionPriority) + { + // Cannot use 'OverloadResolutionPriorityAttribute' on this member. + return (null, null); + } + + (attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics); + + if (attributeData.CommonConstructorArguments is [{ ValueInternal: int priority }]) + { + arguments.GetOrCreateData().OverloadResolutionPriority = priority; + + if (!hasAnyDiagnostics) + { + return (attributeData, boundAttribute); + } + } + + return (null, null); + } } return base.EarlyDecodeWellKnownAttribute(ref arguments); @@ -610,6 +632,20 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut { DecodeInterceptsLocationAttribute(arguments); } + else if (attribute.IsTargetAttribute(AttributeDescription.OverloadResolutionPriorityAttribute)) + { + MessageID.IDS_OverloadResolutionPriority.CheckFeatureAvailability(diagnostics, arguments.AttributeSyntaxOpt); + + if (!CanHaveOverloadResolutionPriority) + { + diagnostics.Add(IsOverride + // Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + ? ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToOverride + // Cannot use 'OverloadResolutionPriorityAttribute' on this member. + : ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, + arguments.AttributeSyntaxOpt); + } + } else { var compilation = this.DeclaringCompilation; @@ -1707,5 +1743,8 @@ internal override System.Reflection.MethodImplAttributes ImplementationAttribute return result; } } + + internal override int? TryGetOverloadResolutionPriority() + => GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index d4fbc4a85099d..e6a081ac788c1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -1273,6 +1273,22 @@ internal override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAt return (null, null); } + else if (IsIndexer && CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.OverloadResolutionPriorityAttribute)) + { + (attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out var hasAnyDiagnostics); + + if (attributeData.CommonConstructorArguments is [{ ValueInternal: int priority }]) + { + arguments.GetOrCreateData().OverloadResolutionPriority = priority; + + if (!hasAnyDiagnostics) + { + return (attributeData, boundAttribute); + } + } + + return (null, null); + } return base.EarlyDecodeWellKnownAttribute(ref arguments); } @@ -1388,6 +1404,20 @@ protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttribut diagnostics.Add(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, arguments.AttributeSyntaxOpt.Location); } } + else if (attribute.IsTargetAttribute(AttributeDescription.OverloadResolutionPriorityAttribute)) + { + MessageID.IDS_OverloadResolutionPriority.CheckFeatureAvailability(diagnostics, arguments.AttributeSyntaxOpt); + + if (!CanHaveOverloadResolutionPriority) + { + diagnostics.Add(IsOverride + // Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + ? ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToOverride + // Cannot use 'OverloadResolutionPriorityAttribute' on this member. + : ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, + arguments.AttributeSyntaxOpt); + } + } } #nullable enable @@ -1493,6 +1523,12 @@ private void ValidateIndexerNameAttribute(CSharpAttributeData attribute, Attribu } } + internal sealed override int? TryGetOverloadResolutionPriority() + { + Debug.Assert(this.IsIndexer); + return GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority; + } + #endregion #region Completion diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs index 7c30811f783b1..94daafa402ca0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs @@ -826,5 +826,11 @@ internal static ImmutableArray GetPublicSymbols(this Immutable internal static bool ShouldCheckRequiredMembers(this MethodSymbol method) => method is { MethodKind: MethodKind.Constructor, HasSetsRequiredMembers: false }; + + internal static int GetOverloadResolutionPriority(this Symbol symbol) + { + Debug.Assert(symbol is MethodSymbol or PropertySymbol); + return symbol is MethodSymbol method ? method.OverloadResolutionPriority : ((PropertySymbol)symbol).OverloadResolutionPriority; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs index c367bb8026866..752624007b7e6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs @@ -419,11 +419,13 @@ internal bool LoadAndValidateAttributes( { var boundAttribute = boundAttributeArray[i]; Debug.Assert(boundAttribute is not null); + Binder attributeBinder = binders[i]; if (boundAttribute.Constructor is { } ctor) { Binder.CheckRequiredMembersInObjectInitializer(ctor, ImmutableArray.CastUp(boundAttribute.NamedArguments), boundAttribute.Syntax, diagnostics); + attributeBinder.ReportDiagnosticsIfObsolete(diagnostics, ctor, boundAttribute.Syntax, hasBaseReceiver: false); } - NullableWalker.AnalyzeIfNeeded(binders[i], boundAttribute, boundAttribute.Syntax, diagnostics.DiagnosticBag); + NullableWalker.AnalyzeIfNeeded(attributeBinder, boundAttribute, boundAttribute.Syntax, diagnostics.DiagnosticBag); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs index eda8289c49a52..afbe2a2c285c5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs @@ -77,5 +77,7 @@ internal SynthesizedReadOnlyListProperty( internal override bool HasUnscopedRefAttribute => false; internal override ObsoleteAttributeData? ObsoleteAttributeData => null; + + internal override int? TryGetOverloadResolutionPriority() => null; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs index 62660a52671e8..b3bb0c75a8269 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs @@ -56,5 +56,10 @@ internal static bool VerifyOverridesMethodFromObject(MethodSymbol overriding, Sp return reportAnError; } + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 3a326030c0c53..fb38ad7417e9f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -320,6 +320,11 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } + internal override int? TryGetOverloadResolutionPriority() + { + return null; + } + /// A synthesized entrypoint that forwards all calls to an async Main Method internal sealed class AsyncForwardEntryPoint : SynthesizedEntryPointSymbol { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs index 58fb705971a5c..2a9d63dbf227c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs @@ -361,5 +361,10 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil builderArgument = null; return false; } + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs index a782661705360..f8587cdaa0746 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs @@ -88,5 +88,10 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil builderArgument = null; return false; } + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index 4509701a923fb..fe9053b3d2ad8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -513,5 +513,10 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil builderArgument = null; return false; } + + internal override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs index 4e5c035c50d1f..bcd7cbafad6e0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs @@ -291,5 +291,10 @@ private static bool IsNullableAnalysisEnabled(CSharpCompilation compilation, Com } return false; } + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs index d96d6f305d923..7533be1e448e6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs @@ -440,5 +440,10 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? bui builderArgument = null; return false; } + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs index 736c6fdbd9ed1..00df312f7b719 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs @@ -364,5 +364,10 @@ internal override bool GenerateDebugInfo internal sealed override bool HasUnscopedRefAttribute => UnderlyingMethod.HasUnscopedRefAttribute; internal sealed override bool UseUpdatedEscapeRules => UnderlyingMethod.UseUpdatedEscapeRules; + + internal sealed override int? TryGetOverloadResolutionPriority() + { + return UnderlyingMethod.TryGetOverloadResolutionPriority(); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs index 8ffb1ce4fdd59..6f8c67a69891f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs @@ -188,5 +188,7 @@ internal override bool HasRuntimeSpecialName return _underlyingProperty.HasRuntimeSpecialName; } } + + internal override int? TryGetOverloadResolutionPriority() => _underlyingProperty.OverloadResolutionPriority; } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 7dd87733508df..88b872d0d7153 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -332,6 +332,16 @@ Atribut AsyncMethodBuilder je u anonymních metod bez explicitního návratového typu zakázaný. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} Vstupní řetězec nelze převést na ekvivalentní reprezentaci bajtů UTF-8. {0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access přístup k elementu ukazatele diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 55181136888ae..e6e2b51d87cc4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -332,6 +332,16 @@ Das AsyncMethodBuilder-Attribut ist für anonyme Methoden ohne expliziten Rückgabetyp unzulässig. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} Die Eingabezeichenfolge kann nicht in die entsprechende UTF-8-Byte-Darstellung konvertiert werden. {0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access Zeigerelementzugriff diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 3dd9f3e74fed7..651740eeed1d4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -332,6 +332,16 @@ El atributo AsyncMethodBuilder no se permite en métodos anónimos sin un tipo de valor devuelto explícito. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} La cadena de entrada no se puede convertir en la representación de bytes UTF-8 equivalente. {0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access acceso al elemento de puntero diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 781aa62b616df..a746de2088114 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -332,6 +332,16 @@ L'attribut AsyncMethodBuilder n'est pas autorisé pour les méthodes anonymes sans type de retour explicite. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} La chaîne d’entrée ne peut pas être convertie en représentation d’octet UTF-8 équivalente. {0} @@ -2592,6 +2602,11 @@ <manquant> + + overload resolution priority + overload resolution priority + + pointer element access accès à l’élément de pointeur diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index fa824a5f72321..aa6b845bd0d1f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -332,6 +332,16 @@ L'attributo AsyncMethodBuilder non è consentito in metodi anonimi senza un tipo restituito esplicito. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} Impossibile convertire la stringa di input nella rappresentazione di UTF-8 byte equivalente. {0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access accesso all'elemento puntatore diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 8fbb34380525e..3c8ac1aacb47c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -332,6 +332,16 @@ AsyncMethodBuilder 属性は、明示的な戻り値の型のない匿名メソッドでは許可されていません。 + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} 入力文字列を同等の UTF-8 バイト表現に変換できません。{0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access ポインター要素アクセス diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 3431529f06c44..3b6c5cde76da0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -332,6 +332,16 @@ AsyncMethodBuilder 특성은 명시적 반환 형식이 없는 익명 메서드에서 허용되지 않습니다. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} 입력 문자열은 해당 UTF-8 바이트 표현으로 변환할 수 없습니다. {0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access 포인터 요소 액세스 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 51594ec3d1bdc..5556620f240c2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -332,6 +332,16 @@ Atrybut AsyncMethodBuilder jest niedozwolony w metodach anonimowych bez jawnego zwracanego typu. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} Nie można przekonwertować ciągu wejściowego na równoważną reprezentację bajtową UTF-8. {0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access dostęp do elementu wskaźnika diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 6f2a69a91670d..a49fc5a48251d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -332,6 +332,16 @@ O atributo AsyncMethodBuilder não é permitido em métodos anônimos sem um tipo de retorno explícito. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} A cadeia de caracteres de entrada não pode ser convertida na representação equivalente em bytes UTF-8. {0} @@ -2592,6 +2602,11 @@ <ausente> + + overload resolution priority + overload resolution priority + + pointer element access acesso ao elemento de ponteiro diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index ad9ab6ad36aed..b4f0f266e6dab 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -332,6 +332,16 @@ Атрибут AsyncMethodBuilder запрещен для анонимных методов без явного типа возвращаемого значения. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} Невозможно преобразовать входную строку в эквивалентное байтовое представление UTF-8.{0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access доступ к элементу указателя diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 350ae25af58e7..3c61e21a671b5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -332,6 +332,16 @@ Açık dönüş türü olmadan, anonim yöntemlerde AsyncMethodBuilder özniteliğine izin verilmez. + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} Giriş dizesi eşdeğer UTF-8 bayt gösterimine dönüştürülemiyor. {0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access işaretçi öğesi erişimi diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index b374e7f23c454..f467244c78602 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -332,6 +332,16 @@ 没有显式返回类型的匿名方法不允许使用 AsyncMethodBuilder 属性。 + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} 无法将输入字符串转换为等效的 UTF-8 字节表示形式。 {0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access 指针元素访问 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index ef978196d298e..49e5c96304da1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -332,6 +332,16 @@ 沒有明確傳回型別的匿名方法上不允許 AsyncMethodBuilder 屬性。 + + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + Cannot use 'OverloadResolutionPriorityAttribute' on this member. + + + + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + + The input string cannot be converted into the equivalent UTF-8 byte representation. {0} 輸入字串無法轉換成對等的 UTF-8 位元組表示法。{0} @@ -2592,6 +2602,11 @@ <missing> + + overload resolution priority + overload resolution priority + + pointer element access 指標元素存取 diff --git a/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs new file mode 100644 index 0000000000000..600ef85738564 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs @@ -0,0 +1,2592 @@ +// 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 System; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.CSharp.UnitTests; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Test; + +public class OverloadResolutionPriorityTests : CSharpTestBase +{ + [Theory, CombinatorialData] + public void IncreasedPriorityWins_01(bool useMetadataReference, bool i1First) + { + var executable = """ + I3 i3 = null; + C.M(i3); + """; + + var i1Source = """ + [OverloadResolutionPriority(1)] + public static void M(I1 x) => System.Console.WriteLine(1); + """; + + var i2Source = """ + public static void M(I2 x) => throw null; + """; + + var source = $$""" + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public class C + { + {{(i1First ? i1Source : i2Source)}} + {{(i1First ? i2Source : i1Source)}} + } + """; + + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1", symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "1").VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetMember("C"); + var ms = c.GetMembers("M").Cast(); + foreach (var m in ms) + { + if (m.Parameters[0].Type.Name == "I1") + { + Assert.Equal(1, m.OverloadResolutionPriority); + } + else + { + Assert.Equal(0, m.OverloadResolutionPriority); + } + } + }; + } + + [Theory, CombinatorialData] + public void IncreasedPriorityWins_02(bool useMetadataReference) + { + var executable = """ + I3 i3 = null; + C.M(i3); + """; + + var source = """ + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public class C + { + [OverloadResolutionPriority(2)] + public static void M(object o) => System.Console.WriteLine(1); + + [OverloadResolutionPriority(1)] + public static void M(I1 x) => throw null; + + public static void M(I2 x) => throw null; + } + """; + + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "1").VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void DecreasedPriorityLoses(bool useMetadataReference, bool i1First) + { + var executable = """ + I3 i3 = null; + C.M(i3); + """; + + var i1Source = """ + public static void M(I1 x) => System.Console.WriteLine(1); + """; + + var i2Source = """ + [OverloadResolutionPriority(-1)] + public static void M(I2 x) => throw null; + """; + + var source = $$""" + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public class C + { + {{(i1First ? i1Source : i2Source)}} + {{(i1First ? i2Source : i1Source)}} + } + """; + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "1").VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void ZeroIsTreatedAsDefault(bool useMetadataReference) + { + var executable = """ + I3 i3 = null; + C.M(i3); + """; + + var source = """ + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public class C + { + public static void M(I1 x) => System.Console.WriteLine(1); + + [OverloadResolutionPriority(0)] + public static void M(I2 x) => throw null; + } + """; + CreateCompilation([executable, source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (2,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(I1)' and 'C.M(I2)' + // C.M(i3); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(I1)", "C.M(I2)").WithLocation(2, 3) + ); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + + CreateCompilation(executable, references: [AsReference(comp, useMetadataReference)]).VerifyDiagnostics( + // (2,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(I1)' and 'C.M(I2)' + // C.M(i3); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(I1)", "C.M(I2)").WithLocation(2, 3) + ); + + static void validate(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetMember("C"); + var ms = c.GetMembers("M").Cast(); + Assert.All(ms, m => Assert.Equal(0, m.OverloadResolutionPriority)); + } + } + + [Theory] + [InlineData(-1)] + [InlineData(1)] + [InlineData(int.MaxValue)] + [InlineData(int.MinValue)] + public void AmbiguityWithinPriority(int priority) + { + var source = $$""" + using System.Runtime.CompilerServices; + + I3 i3 = null; + C.M(i3); + + interface I1 {} + interface I2 {} + interface I3 : I1, I2 {} + + class C + { + [OverloadResolutionPriority({{priority}})] + public static void M(I1 x) => System.Console.WriteLine(1); + + [OverloadResolutionPriority({{priority}})] + public static void M(I2 x) => throw null; + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (4,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(I1)' and 'C.M(I2)' + // C.M(i3); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(I1)", "C.M(I2)").WithLocation(4, 3) + ); + } + + [Theory, CombinatorialData] + public void MethodDiscoveryStopsAtFirstApplicableMethod(bool useMetadataReference) + { + var @base = """ + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public class Base + { + [OverloadResolutionPriority(1)] + public static void M(I2 x) => throw null; + } + """; + + var derived = """ + public class Derived : Base + { + public static void M(I1 x) => System.Console.WriteLine(1); + } + """; + + var executable = """ + I3 i3 = null; + Derived.M(i3); + """; + CompileAndVerify([executable, @base, derived, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + + var baseComp = CreateCompilation([@base, OverloadResolutionPriorityAttributeDefinition]); + var baseReference = useMetadataReference ? baseComp.ToMetadataReference() : baseComp.EmitToImageReference(); + + var derivedComp = CreateCompilation(derived, references: [baseReference]); + var derivedReference = useMetadataReference ? derivedComp.ToMetadataReference() : derivedComp.EmitToImageReference(); + + CompileAndVerify(executable, references: [baseReference, derivedReference], expectedOutput: "1").VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void MethodDiscoveryStopsAtFirstApplicableIndexer(bool useMetadataReference) + { + var @base = """ + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public class Base + { + [OverloadResolutionPriority(1)] + public int this[I2 x] + { + get => throw null; + set => throw null; + } + } + """; + + var derived = """ + public class Derived : Base + { + public int this[I1 x] + { + get { System.Console.Write(1); return 1; } + set => System.Console.Write(2); + } + } + """; + + var executable = """ + I3 i3 = null; + var d = new Derived(); + _ = d[i3]; + d[i3] = 0; + """; + CompileAndVerify([executable, @base, derived, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "12").VerifyDiagnostics(); + + var baseComp = CreateCompilation([@base, OverloadResolutionPriorityAttributeDefinition]); + var baseReference = useMetadataReference ? baseComp.ToMetadataReference() : baseComp.EmitToImageReference(); + + var derivedComp = CreateCompilation(derived, references: [baseReference]); + var derivedReference = useMetadataReference ? derivedComp.ToMetadataReference() : derivedComp.EmitToImageReference(); + + CompileAndVerify(executable, references: [baseReference, derivedReference], expectedOutput: "12").VerifyDiagnostics(); + } + + [Fact] + public void OrderingWithAnExtensionMethodContainingClass() + { + var source = """ + using System.Runtime.CompilerServices; + + I3 i3 = null; + i3.M(); + + interface I1 {} + interface I2 {} + interface I3 : I1, I2 {} + + static class C + { + [OverloadResolutionPriority(1)] + public static void M(this I1 x) => System.Console.WriteLine(1); + + public static void M(this I2 x) => throw null; + } + """; + CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + } + + [Fact] + public void DoesNotOrderBetweenExtensionMethodContainingClasses() + { + var source = """ + using System.Runtime.CompilerServices; + + I3 i3 = null; + i3.M(); + + interface I1 {} + interface I2 {} + interface I3 : I1, I2 {} + + static class C1 + { + [OverloadResolutionPriority(1)] + public static void M(this I1 x) => System.Console.WriteLine(1); + } + + static class C2 + { + public static void M(this I2 x) => throw null; + } + """; + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (4,4): error CS0121: The call is ambiguous between the following methods or properties: 'C1.M(I1)' and 'C2.M(I2)' + // i3.M(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C1.M(I1)", "C2.M(I2)").WithLocation(4, 4) + ); + } + + [Fact] + public void Overrides_NoPriorityChangeFromBase_Methods() + { + var code = """ + using System.Runtime.CompilerServices; + + var d = new Derived(); + d.M("test"); + + public class Base + { + [OverloadResolutionPriority(1)] + public virtual void M(object o) => throw null; + public virtual void M(string s) => throw null; + } + + public class Derived : Base + { + public override void M(object o) => System.Console.WriteLine("1"); + public override void M(string s) => throw null; + } + """; + + CompileAndVerify([code, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + } + + [Fact] + public void Overrides_ChangePriorityInSource_Methods() + { + var executable = """ + var d = new Derived(); + d.M("test"); + """; + + var code = """ + using System.Runtime.CompilerServices; + + public class Base + { + [OverloadResolutionPriority(1)] + public virtual void M(object o) => System.Console.WriteLine("1"); + public virtual void M(string s) => throw null; + } + + public class Derived : Base + { + [OverloadResolutionPriority(0)] + public override void M(object o) => System.Console.WriteLine("1"); + [OverloadResolutionPriority(2)] + public override void M(string s) => throw null; + } + """; + + var comp = CreateCompilation([executable, code, OverloadResolutionPriorityAttributeDefinition]); + + var expectedErrors = new[] { + // (12,6): error CS9500: Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + // [OverloadResolutionPriority(0)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToOverride, "OverloadResolutionPriority(0)").WithLocation(12, 6), + // (14,6): error CS9500: Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + // [OverloadResolutionPriority(2)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToOverride, "OverloadResolutionPriority(2)").WithLocation(14, 6) + }; + + comp.VerifyDiagnostics(expectedErrors); + verify(comp); + + var comp2 = CreateCompilation([code, OverloadResolutionPriorityAttributeDefinition]); + comp2.VerifyDiagnostics(expectedErrors); + comp = CreateCompilation(executable, references: [comp2.ToMetadataReference()]); + comp.VerifyDiagnostics(); + verify(comp); + + static void verify(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + + var method = (IMethodSymbol)model.GetSymbolInfo(invocation).Symbol!; + Assert.Equal("void Derived.M(System.Object o)", method.ToTestDisplayString()); + } + } + + [Fact] + public void Overrides_ChangePriorityInMetadata_Methods() + { + var source1 = """ + using System.Runtime.CompilerServices; + + public class Base + { + [OverloadResolutionPriority(1)] + public virtual void M(object o) => System.Console.WriteLine("1"); + public virtual void M(string s) => throw null; + } + """; + + var comp1 = CreateCompilation([source1, OverloadResolutionPriorityAttributeDefinition], assemblyName: "assembly1"); + var assembly1 = comp1.EmitToImageReference(); + + // Equivalent to: + // + // public class Derived : Base + // { + // [OverloadResolutionPriority(0)] + // public override void M(object o) => System.Console.WriteLine("1"); + // [OverloadResolutionPriority(2)] + // public override void M(string s) => throw null; + // } + var il2 = """ + .assembly extern assembly1 {} + + .class public auto ansi beforefieldinit Derived extends [assembly1]Base + { + .method public hidebysig virtual + instance void M ( + object o + ) cil managed + { + .custom instance void [assembly1]System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 00 00 00 00 00 00 + ) + ldstr "1" + call void [mscorlib]System.Console::WriteLine(string) + ret + } // end of method Derived::M + + .method public hidebysig virtual + instance void M ( + string s + ) cil managed + { + .custom instance void [assembly1]System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 02 00 00 00 00 00 + ) + ldnull + throw + } // end of method Derived::M + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [assembly1]Base::.ctor() + ret + } // end of method Derived::.ctor + + } + """; + var assembly2 = CompileIL(il2); + + var code = """ + var d = new Derived(); + d.M("test"); + """; + + CompileAndVerify(code, references: [assembly1, assembly2], expectedOutput: "1").VerifyDiagnostics(); + } + + [Fact] + public void Overrides_ChangePriorityInMetadata_Indexers() + { + var source1 = """ + using System.Runtime.CompilerServices; + + public class Base + { + [OverloadResolutionPriority(1)] + public virtual int this[object o] + { + get => throw null; + set => throw null; + } + public virtual int this[string s] + { + get => throw null; + set => throw null; + } + } + """; + + var comp1 = CreateCompilation([source1, OverloadResolutionPriorityAttributeDefinition], assemblyName: "assembly1"); + var assembly1 = comp1.EmitToImageReference(); + + // Equivalent to: + // + // public class Derived: Base + // { + // public override int this[object o] + // { + // get { System.Console.Write(1); return 1; } + // set => System.Console.Write(2); + // } + // [OverloadResolutionPriority(2)] + // public override int this[string s] + // { + // get => throw null; + // set => throw null; + // } + // } + var il2 = """ + .assembly extern assembly1 {} + + .class public auto ansi beforefieldinit Derived extends [assembly1]Base + { + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + // Methods + .method public hidebysig specialname virtual + instance int32 get_Item ( + object o + ) cil managed + { + ldc.i4.1 + call void [mscorlib]System.Console::Write(int32) + ldc.i4.1 + ret + } // end of method Derived::get_Item + + .method public hidebysig specialname virtual + instance void set_Item ( + object o, + int32 'value' + ) cil managed + { + ldc.i4.2 + call void [mscorlib]System.Console::Write(int32) + ret + } // end of method Derived::set_Item + + .method public hidebysig specialname virtual + instance int32 get_Item ( + string s + ) cil managed + { + ldnull + throw + } // end of method Derived::get_Item + + .method public hidebysig specialname virtual + instance void set_Item ( + string s, + int32 'value' + ) cil managed + { + ldnull + throw + } // end of method Derived::set_Item + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [assembly1]Base::.ctor() + ret + } // end of method Derived::.ctor + + // Properties + .property instance int32 Item( + object o + ) + { + .get instance int32 Derived::get_Item(object) + .set instance void Derived::set_Item(object, int32) + } + .property instance int32 Item( + string s + ) + { + .custom instance void [assembly1]System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 02 00 00 00 00 00 + ) + .get instance int32 Derived::get_Item(string) + .set instance void Derived::set_Item(string, int32) + } + } + """; + var assembly2 = CompileIL(il2); + + var code = """ + var d = new Derived(); + _ = d["test"]; + d["test"] = 0; + """; + + CompileAndVerify(code, references: [assembly1, assembly2], expectedOutput: "12").VerifyDiagnostics(); + } + + [Fact] + public void ThroughRetargeting_Methods() + { + var source1 = """ + public class RetValue {} + """; + + var comp1_1 = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), source1, TargetFrameworkUtil.StandardReferences); + var comp1_2 = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), source1, TargetFrameworkUtil.StandardReferences); + + var source2 = """ + using System.Runtime.CompilerServices; + + public class C + { + [OverloadResolutionPriority(1)] + public RetValue M(object o) + { + System.Console.WriteLine("1"); + return new(); + } + public RetValue M(string s) => throw null; + } + """; + + var comp2 = CreateCompilation([source2, OverloadResolutionPriorityAttributeDefinition], references: [comp1_1.ToMetadataReference()], targetFramework: TargetFramework.Standard); + comp2.VerifyDiagnostics(); + + var source3 = """ + var c = new C(); + c.M("test"); + """; + + var comp3 = CreateCompilation(source3, references: [comp2.ToMetadataReference(), comp1_2.ToMetadataReference()], targetFramework: TargetFramework.Standard); + comp3.VerifyDiagnostics(); + + var c = comp3.GetTypeByMetadataName("C")!; + var ms = c.GetMembers("M"); + Assert.Equal(2, ms.Length); + Assert.All(ms, m => Assert.IsType(m)); + + var tree = comp3.SyntaxTrees[0]; + var model = comp3.GetSemanticModel(tree); + var invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + var method = (IMethodSymbol)model.GetSymbolInfo(invocation).Symbol!; + Assert.Equal("RetValue C.M(System.Object o)", method.ToTestDisplayString()); + } + + [Fact] + public void ThroughRetargeting_Indexers() + { + var source1 = """ + public class RetValue {} + """; + + var comp1_1 = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), source1, TargetFrameworkUtil.StandardReferences); + var comp1_2 = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), source1, TargetFrameworkUtil.StandardReferences); + + var source2 = """ + using System.Runtime.CompilerServices; + + public class C + { + [OverloadResolutionPriority(1)] + public RetValue this[object o] + { + get + { + System.Console.WriteLine("1"); + return new(); + } + set + { + System.Console.WriteLine("2"); + } + } + public RetValue this[string s] + { + get => throw null; + set => throw null; + } + } + """; + + var comp2 = CreateCompilation([source2, OverloadResolutionPriorityAttributeDefinition], references: [comp1_1.ToMetadataReference()], targetFramework: TargetFramework.Standard); + comp2.VerifyDiagnostics(); + + var source3 = """ + var c = new C(); + c["test"] = new(); + _ = c["test"]; + """; + + var comp3 = CreateCompilation(source3, references: [comp2.ToMetadataReference(), comp1_2.ToMetadataReference()], targetFramework: TargetFramework.Standard); + comp3.VerifyDiagnostics(); + + var c = comp3.GetTypeByMetadataName("C")!; + var indexers = c.Indexers; + Assert.Equal(2, indexers.Length); + Assert.All(indexers, m => Assert.IsType(m)); + + var tree = comp3.SyntaxTrees[0]; + var model = comp3.GetSemanticModel(tree); + var accesses = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, accesses.Length); + AssertEx.Equal("RetValue C.this[System.Object o] { get; set; }", model.GetSymbolInfo(accesses[0]).Symbol!.ToTestDisplayString()); + AssertEx.Equal("RetValue C.this[System.Object o] { get; set; }", model.GetSymbolInfo(accesses[1]).Symbol!.ToTestDisplayString()); + } + + [Fact] + public void ThroughRetargeting_Constructors() + { + var source1 = """ + public class Base {} + """; + + var comp1_1 = CreateCompilation(new AssemblyIdentity("Base", new Version(1, 0, 0, 0), isRetargetable: true), source1, TargetFrameworkUtil.StandardReferences); + var comp1_2 = CreateCompilation(new AssemblyIdentity("Base", new Version(2, 0, 0, 0), isRetargetable: true), source1, TargetFrameworkUtil.StandardReferences); + + var source2 = """ + using System.Runtime.CompilerServices; + + public class Derived : Base + { + [OverloadResolutionPriority(1)] + public Derived(object o) + { + } + public Derived(string s) + { + } + } + """; + + var comp2 = CreateCompilation([source2, OverloadResolutionPriorityAttributeDefinition], references: [comp1_1.ToMetadataReference()]); + comp2.VerifyDiagnostics(); + + var source3 = """ + var c = new Derived("test"); + """; + + var comp3 = CreateCompilation(source3, references: [comp2.ToMetadataReference(), comp1_2.ToMetadataReference()]); + comp3.VerifyDiagnostics(); + + var derived = comp3.GetTypeByMetadataName("Derived")!; + var constructors = derived.Constructors; + Assert.Equal(2, constructors.Length); + Assert.All(constructors, m => Assert.IsType(m)); + + var tree = comp3.SyntaxTrees[0]; + var model = comp3.GetSemanticModel(tree); + var creation = tree.GetRoot().DescendantNodes().OfType().Single(); + AssertEx.Equal("Derived..ctor(System.Object o)", model.GetSymbolInfo(creation).Symbol!.ToTestDisplayString()); + } + + [Fact] + public void LangVersion() + { + var source = """ + using System.Runtime.CompilerServices; + + public class C + { + [OverloadResolutionPriority(1)] + public void M(object o) => System.Console.Write("1"); + public void M(string s) => throw null; + + [OverloadResolutionPriority(1)] + public int this[object o] + { + get + { + System.Console.Write("2"); + return 1; + } + set + { + System.Console.Write("3"); + } + } + public int this[string s] + { + get => throw null; + set => throw null; + } + + [OverloadResolutionPriority(1)] + public C(object o) + { + System.Console.Write("4"); + } + + public C(string s) + { + throw null; + } + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition], parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (5,6): error CS9202: Feature 'overload resolution priority' is not available in C# 12.0. Please use language version 13.0 or greater. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "OverloadResolutionPriority(1)").WithArguments("overload resolution priority", "13.0").WithLocation(5, 6), + // (9,6): error CS9202: Feature 'overload resolution priority' is not available in C# 12.0. Please use language version 13.0 or greater. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "OverloadResolutionPriority(1)").WithArguments("overload resolution priority", "13.0").WithLocation(9, 6), + // (28,6): error CS9202: Feature 'overload resolution priority' is not available in C# 12.0. Please use language version 13.0 or greater. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "OverloadResolutionPriority(1)").WithArguments("overload resolution priority", "13.0").WithLocation(28, 6)); + + var definingComp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition], parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + + var consumingSource = """ + var c = new C("test"); + c["test"] = 0; + _ = c["test"]; + c.M("test"); + """; + + // We don't error on consumption, only on definition, so this runs just fine. + CompileAndVerify(consumingSource, references: [definingComp.ToMetadataReference()], parseOptions: TestOptions.Regular12, expectedOutput: "4321").VerifyDiagnostics(); + } + + [Fact] + public void AppliedToAttributeConstructors() + { + var source = """ + using System.Runtime.CompilerServices; + + [C("test")] + public class C : System.Attribute + { + [OverloadResolutionPriority(1)] + public C(object o) {} + + public C(string s) {} + } + """; + + var verifier = CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics(); + var c = ((CSharpCompilation)verifier.Compilation).GetTypeByMetadataName("C"); + + var attr = c!.GetAttributes().Single(); + AssertEx.Equal("C..ctor(System.Object o)", attr.AttributeConstructor.ToTestDisplayString()); + } + + [Fact] + public void CycleOnOverloadResolutionPriorityConstructor_01() + { + var source = """ + namespace System.Runtime.CompilerServices; + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + public sealed class OverloadResolutionPriorityAttribute : Attribute + { + [OverloadResolutionPriority(1)] + public OverloadResolutionPriorityAttribute(int priority) + { + Priority = priority; + } + + public int Priority { get;} + } + """; + + CompileAndVerify(source).VerifyDiagnostics(); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + public void CycleOnOverloadResolutionPriorityConstructor_02(int ctorToForce) + { + var source = """ + namespace System.Runtime.CompilerServices; + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + public sealed class OverloadResolutionPriorityAttribute : Attribute + { + public OverloadResolutionPriorityAttribute(int priority) + { + } + + // Attribute is intentionally ignored, as this will cause a cycle + [OverloadResolutionPriority(1)] + public OverloadResolutionPriorityAttribute(object priority) + { + } + + public int Priority { get;} + } + + interface I1 {} + interface I2 {} + interface I3 : I1, I2 {} + + static class C + { + [OverloadResolutionPriority(1)] + public static void M(this I1 x) => System.Console.WriteLine(1); + + public static void M(this I2 x) => throw null; + + static void Main() + { + I3 i3 = null; + i3.M(); + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var secondCtor = tree.GetRoot().DescendantNodes().OfType().Skip(ctorToForce).First(); + + // Explicitly pull on the attributes to force binding of any attributes. This exposes a potential race condition in early attribute binding. + var ctor = model.GetDeclaredSymbol(secondCtor)!.GetSymbol(); + _ = ctor.GetAttributes(); + + var verifier = CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics(); + var attr = ((CSharpCompilation)verifier.Compilation).GetTypeByMetadataName("System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute"); + var ctors = attr!.GetMembers(".ctor"); + + AssertEx.Equal(["System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute..ctor(System.Int32 priority)", "System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute..ctor(System.Object priority)"], + ctors.SelectAsArray(ctor => ((MethodSymbol)ctor).ToTestDisplayString())); + + var attrs = ctors.SelectAsArray(ctor => ctor.GetAttributes()); + + Assert.Empty(attrs[0]); + AssertEx.Equal("System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute..ctor(System.Int32 priority)", + attrs[1].Single().AttributeConstructor.ToTestDisplayString()); + + verifier.VerifyIL("System.Runtime.CompilerServices.C.Main()", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldnull + IL_0001: call "void System.Runtime.CompilerServices.C.M(System.Runtime.CompilerServices.I1)" + IL_0006: ret + } + """); + } + + [Fact] + public void CycleOnOverloadResolutionPriorityConstructor_03() + { + var source = """ + namespace System.Runtime.CompilerServices; + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + public sealed class OverloadResolutionPriorityAttribute : Attribute + { + [OverloadResolutionPriority(1)] + public OverloadResolutionPriorityAttribute(int priority) + { + Priority = priority; + } + + public required int Priority { get; set; } + } + """; + + CreateCompilation([source, RequiredMemberAttribute, CompilerFeatureRequiredAttribute]).VerifyDiagnostics( + // (6,6): error CS9035: Required member 'OverloadResolutionPriorityAttribute.Priority' must be set in the object initializer or attribute constructor. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "OverloadResolutionPriority").WithArguments("System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute.Priority").WithLocation(6, 6)); + } + + [Fact] + public void CycleOnOverloadResolutionPriorityConstructor_04() + { + var source = """ + namespace System.Runtime.CompilerServices; + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + public sealed class OverloadResolutionPriorityAttribute : Attribute + { + [OtherAttribute()] + public OverloadResolutionPriorityAttribute(int priority) + { + Priority = priority; + } + + public int Priority { get;} + } + + class OtherAttribute : Attribute + { + [OverloadResolutionPriority(1)] + public OtherAttribute() {} + } + """; + + CompileAndVerify(source).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void OverloadResolutionAppliedToIndexers(bool useMetadataReference, bool i1First) + { + var executable = """ + var c = new C(); + I3 i3 = null; + _ = c[i3]; + c[i3] = 0; + """; + + var i1Source = """ + [OverloadResolutionPriority(1)] + public int this[I1 x] + { + get { System.Console.Write(1); return 0; } + set => System.Console.Write(2); + } + """; + + var i2Source = """ + public int this[I2 x] + { + get => throw null; + set => throw null; + } + """; + + var source = $$""" + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public class C + { + {{(i1First ? i1Source : i2Source)}} + {{(i1First ? i2Source : i1Source)}} + } + """; + + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "12").VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "12").VerifyDiagnostics(); + } + + [Fact] + public void AppliedToRegularProperty() + { + var source = """ + using System.Runtime.CompilerServices; + + public class C + { + [OverloadResolutionPriority(1)] + public int P { get; set; } + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (5,6): error CS9501: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(5, 6) + ); + } + + [Fact] + public void AppliedToIndexerOverride() + { + var source = """ + using System.Runtime.CompilerServices; + + class Base + { + public virtual int this[int x] => throw null; + } + + class Derived : Base + { + [OverloadResolutionPriority(1)] + public override int this[int x] => throw null; + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (10,6): error CS9500: Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToOverride, "OverloadResolutionPriority(1)").WithLocation(10, 6) + ); + } + + [Fact] + public void NoImpactToFunctionType() + { + var source = """ + var m = C.M; + + class C + { + [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] + public static void M(string s) {} + + public static void M(int i) {} + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (1,9): error CS8917: The delegate type could not be inferred. + // var m = C.M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "C.M").WithLocation(1, 9) + ); + } + + [Theory, CombinatorialData] + public void DelegateConversion(bool useMetadataReference) + { + var source = """ + using System.Runtime.CompilerServices; + + public class C + { + [OverloadResolutionPriority(1)] + public static void M(object o) => System.Console.Write(1); + public static void M(string s) => throw null; + } + """; + + var executable = """ + System.Action a = C.M; + a(null); + """; + + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "1").VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void RestateOverriddenPriority_Method(bool useMetadataReference) + { + var @base = """ + using System.Runtime.CompilerServices; + + public class Base + { + [OverloadResolutionPriority(1)] + public virtual void M(object o) => throw null; + public virtual void M(string s) => throw null; + } + """; + + var derived = """ + using System.Runtime.CompilerServices; + public class Derived : Base + { + [OverloadResolutionPriority(1)] + public override void M(object o) => throw null; + public override void M(string s) => throw null; + } + """; + + var expectedDiagnostics = new[] + { + // (4,6): error CS9500: Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToOverride, "OverloadResolutionPriority(1)").WithLocation(4, 6) + }; + + CreateCompilation([derived, @base, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics(expectedDiagnostics); + + var baseComp = CreateCompilation([@base, OverloadResolutionPriorityAttributeDefinition]); + CreateCompilation(derived, references: [useMetadataReference ? baseComp.ToMetadataReference() : baseComp.EmitToImageReference()]).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void Interface_DifferentPriorities_Methods(bool useMetadataReference) + { + var executable = """ + var c = new C(); + I3 i3 = null; + c.M(i3); + ((I)c).M(i3); + """; + + var source = """ + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public interface I + { + [OverloadResolutionPriority(1)] + void M(I1 i1); + void M(I2 i2); + } + public class C : I + { + public void M(I1 i1) => System.Console.Write(1); + [OverloadResolutionPriority(2)] + public void M(I2 i2) => System.Console.Write(2); + } + """; + + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "21").VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "21").VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void Interface_DifferentPriorities_Indexers(bool useMetadataReference) + { + var executable = """ + var c = new C(); + I3 i3 = null; + c[i3] = 1; + _ = c[i3]; + ((I)c)[i3] = 1; + _ = ((I)c)[i3]; + """; + + var source = """ + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public interface I + { + [OverloadResolutionPriority(1)] + int this[I1 i1] { get; set; } + int this[I2 i2] { get; set; } + } + public class C : I + { + public int this[I1 i1] + { + get + { + System.Console.Write(1); + return 1; + } + set => System.Console.Write(2); + } + [OverloadResolutionPriority(1)] + public int this[I2 i2] + { + get + { + System.Console.Write(3); + return 1; + } + set => System.Console.Write(4); + } + } + """; + + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "4321").VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "4321").VerifyDiagnostics(); + } + + [Fact] + public void AppliedToIndexerGetterSetter_Source() + { + var source = """ + using System.Runtime.CompilerServices; + public class C + { + public int this[int x] + { + [OverloadResolutionPriority(1)] + get => throw null; + [OverloadResolutionPriority(1)] + set => throw null; + } + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (6,10): error CS9502: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(6, 10), + // (8,10): error CS9502: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(8, 10) + ); + + var c = comp.GetTypeByMetadataName("C")!; + var indexer = c.GetMember("this[]"); + + Assert.Equal(0, indexer.OverloadResolutionPriority); + Assert.Equal(0, indexer.GetMethod.OverloadResolutionPriority); + Assert.Equal(0, indexer.SetMethod.OverloadResolutionPriority); + } + + [Fact] + public void AppliedToIndexerGetterSetter_Metadata() + { + // Equivalent to: + // public class C + // { + // public int this[object x] + // { + // [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] + // get => throw null; + // [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] + // set => throw null; + // } + // public int this[string x] + // { + // get { System.Console.Write(1); return 1; } + // set => System.Console.Write(2); + // } + // } + var il = """ + .class public auto ansi beforefieldinit C + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + // Methods + .method public hidebysig specialname + instance int32 get_Item ( + object x + ) cil managed + { + .custom instance void System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 01 00 00 00 00 00 + ) + ldnull + throw + } // end of method C::get_Item + + .method public hidebysig specialname + instance void set_Item ( + object x, + int32 'value' + ) cil managed + { + .custom instance void System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 01 00 00 00 00 00 + ) + ldnull + throw + } // end of method C::set_Item + + .method public hidebysig specialname + instance int32 get_Item ( + string x + ) cil managed + { + ldc.i4.1 + call void [mscorlib]System.Console::Write(int32) + ldc.i4.1 + ret + } // end of method C::get_Item + + .method public hidebysig specialname + instance void set_Item ( + string x, + int32 'value' + ) cil managed + { + ldc.i4.2 + call void [mscorlib]System.Console::Write(int32) + ret + } // end of method C::set_Item + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } // end of method C::.ctor + + // Properties + .property instance int32 Item( + object x + ) + { + .get instance int32 C::get_Item(object) + .set instance void C::set_Item(object, int32) + } + .property instance int32 Item( + string x + ) + { + .get instance int32 C::get_Item(string) + .set instance void C::set_Item(string, int32) + } + } + + """; + + var ilRef = CompileIL(il + OverloadResolutionPriorityAttributeILDefinition); + + var source = """ + var c = new C(); + _ = c["test"]; + c["test"] = 0; + """; + + var comp = (CSharpCompilation)CompileAndVerify(source, references: [ilRef], expectedOutput: "12").VerifyDiagnostics().Compilation; + + var c = comp.GetTypeByMetadataName("C")!; + var indexers = c.GetMembers("this[]"); + + Assert.Equal(2, indexers.Length); + + var indexer = (PropertySymbol)indexers[0]; + AssertEx.Equal("System.Int32 C.this[System.Object x] { get; set; }", indexer.ToTestDisplayString()); + Assert.Equal(0, indexer.OverloadResolutionPriority); + Assert.Equal(0, indexer.GetMethod.OverloadResolutionPriority); + Assert.Equal(0, indexer.SetMethod.OverloadResolutionPriority); + + indexer = (PropertySymbol)indexers[1]; + AssertEx.Equal("System.Int32 C.this[System.String x] { get; set; }", indexer.ToTestDisplayString()); + Assert.Equal(0, indexer.OverloadResolutionPriority); + Assert.Equal(0, indexer.GetMethod.OverloadResolutionPriority); + Assert.Equal(0, indexer.SetMethod.OverloadResolutionPriority); + } + + [Fact] + public void AppliedToPropertyGetterSetter() + { + var source = """ + using System.Runtime.CompilerServices; + public class C + { + public int Prop + { + [OverloadResolutionPriority(1)] + get => throw null; + [OverloadResolutionPriority(1)] + set => throw null; + } + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (6,10): error CS9502: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(6, 10), + // (8,10): error CS9502: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(8, 10) + ); + + var c = comp.GetTypeByMetadataName("C")!; + var indexer = c.GetMember("Prop"); + + Assert.Equal(0, indexer.OverloadResolutionPriority); + Assert.Equal(0, indexer.GetMethod.OverloadResolutionPriority); + Assert.Equal(0, indexer.SetMethod.OverloadResolutionPriority); + } + + [Fact] + public void AppliedToEventGetterSetter() + { + var source = """ + using System.Runtime.CompilerServices; + public class C + { + public event System.Action Prop + { + [OverloadResolutionPriority(1)] + add { } + [OverloadResolutionPriority(1)] + remove { } + } + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (6,10): error CS9502: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(6, 10), + // (8,10): error CS9502: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(8, 10) + ); + + var c = comp.GetTypeByMetadataName("C")!; + var indexer = c.GetMember("Prop"); + + Assert.Equal(0, indexer!.AddMethod!.OverloadResolutionPriority); + Assert.Equal(0, indexer!.RemoveMethod!.OverloadResolutionPriority); + } + + [Fact] + public void ExplicitImplementation_AppliedToImplementation() + { + var source = """ + using System.Runtime.CompilerServices; + public interface I + { + void M(object o); + void M(string s); + + int this[object o] { get; set; } + int this[string s] { get; set; } + } + + public class C : I + { + [OverloadResolutionPriority(1)] + void I.M(object o) => throw null; + void I.M(string s) => throw null; + + [OverloadResolutionPriority(1)] + int I.this[object o] + { + get => throw null; + set => throw null; + } + int I.this[string s] + { + get => throw null; + set => throw null; + } + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (13,6): error CS9503: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(13, 6), + // (17,6): error CS9503: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(17, 6) + ); + } + + [Fact] + public void ExplicitImplementation_AppliedToInterface() + { + var source = """ + using System.Runtime.CompilerServices; + + var c = new C(); + ((I)c).M("test"); + _ = ((I)c)["test"]; + ((I)c)["test"] = 1; + + public interface I + { + [OverloadResolutionPriority(1)] + void M(object o); + void M(string s); + + [OverloadResolutionPriority(1)] + int this[object o] { get; set; } + int this[string s] { get; set; } + } + + public class C : I + { + void I.M(object o) => System.Console.Write(1); + void I.M(string s) => throw null; + + int I.this[object o] + { + get { System.Console.Write(2); return 1; } + set => System.Console.Write(3); + } + int I.this[string s] + { + get => throw null; + set => throw null; + } + } + """; + + CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "123").VerifyDiagnostics(); + } + + [Fact] + public void ExplicitImplementation_AppliedToBoth() + { + var source = """ + using System.Runtime.CompilerServices; + public interface I + { + [OverloadResolutionPriority(1)] + void M(object o); + void M(string s); + + [OverloadResolutionPriority(1)] + int this[object o] { get; set; } + int this[string s] { get; set; } + } + + public class C : I + { + [OverloadResolutionPriority(1)] + void I.M(object o) => throw null; + void I.M(string s) => throw null; + + [OverloadResolutionPriority(1)] + int I.this[object o] + { + get => throw null; + set => throw null; + } + int I.this[string s] + { + get => throw null; + set => throw null; + } + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (15,6): error CS9503: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(15, 6), + // (19,6): error CS9503: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(19, 6) + ); + } + + [Theory, CombinatorialData] + public void Dynamic(bool useMetadataReference) + { + var source = @" + using System.Runtime.CompilerServices; + + public class C + { + [OverloadResolutionPriority(1)] + public C(object o) => throw null; + public C(string s) => System.Console.Write(1); + + [OverloadResolutionPriority(1)] + public void M(object o) => throw null; + public void M(string s) => System.Console.Write(2); + + [OverloadResolutionPriority(1)] + public int this[object o] + { + get => throw null; + set => throw null; + } + public int this[string o] + { + get { System.Console.Write(3); return 1; } + set => System.Console.Write(4); + } + } + "; + + var executable = """ + dynamic arg = "test"; + C c = new C(arg); + c.M(arg); + _ = c[arg]; + c[arg] = 1; + """; + + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], targetFramework: TargetFramework.Mscorlib45AndCSharp, expectedOutput: "1234").VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition], targetFramework: TargetFramework.Mscorlib45AndCSharp); + CompileAndVerify(executable, references: new[] { AsReference(comp, useMetadataReference) }, targetFramework: TargetFramework.Mscorlib45AndCSharp, expectedOutput: "1234").VerifyDiagnostics(); + } + + [Fact] + public void Destructor() + { + var source = """ + using System.Runtime.CompilerServices; + + public class C + { + [OverloadResolutionPriority(1)] + ~C() => throw null; + } + """; + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (5,6): error CS9501: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(5, 6) + ); + } + [Fact] + public void IndexedProperty() + { + var attrRef = CreateCompilation(OverloadResolutionPriorityAttributeDefinition); + + string vbSource = """ + Imports System + Imports System.Runtime.CompilerServices + Imports System.Runtime.InteropServices + + + Public Class B + + Public Property IndexedProperty(arg As Object) As Integer + Get + Return 0 + End Get + Set + End Set + End Property + Public Property IndexedProperty(arg As String) As Integer + Get + Throw New Exception() + End Get + Set + Throw New Exception() + End Set + End Property + End Class + """; + var vb = CreateVisualBasicCompilation(GetUniqueName(), vbSource, referencedAssemblies: TargetFrameworkUtil.NetStandard20References, referencedCompilations: [attrRef]); + var vbRef = vb.EmitToImageReference(); + + var executable = """ + var b = new B(); + _ = b.IndexedProperty["test"]; + b.IndexedProperty["test"] = 0; + """; + + var verifier = CompileAndVerify(executable, references: [attrRef.EmitToImageReference(), vbRef]).VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 29 (0x1d) + .maxstack 3 + IL_0000: newobj "B..ctor()" + IL_0005: dup + IL_0006: ldstr "test" + IL_000b: callvirt "int B.IndexedProperty[object].get" + IL_0010: pop + IL_0011: ldstr "test" + IL_0016: ldc.i4.0 + IL_0017: callvirt "void B.IndexedProperty[object].set" + IL_001c: ret + } + """); + } + + [Fact] + public void NoWarningOnLocalFunction() + { + var source = """ + using System.Runtime.CompilerServices; + + public class C + { + public static void Main() + { + Local("test"); + + [OverloadResolutionPriority(1)] + void Local(object o) => System.Console.Write(1); + } + } + """; + + CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + } + + [Fact] + public void ErrorOnLambda() + { + var source = """ + using System.Runtime.CompilerServices; + + public class C + { + public void M() + { + var l = [OverloadResolutionPriority(1)] (object o) => System.Console.Write(1); + } + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (7,18): error CS9501: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // var l = [OverloadResolutionPriority(1)] (object o) => System.Console.Write(1); + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(7, 18) + ); + } + + [Theory, CombinatorialData] + public void BinaryOperators_SameType(bool useMetadataReference) + { + var source = """ + using System.Runtime.CompilerServices; + + public interface I1 {} + public interface I2 {} + public interface I3 : I1, I2 {} + + public class C + { + [OverloadResolutionPriority(1)] + public static C operator +(C c, I1 i) { System.Console.Write(1); return c; } + public static C operator +(C c, I2 i) => throw null; + } + """; + + var executable = """ + var c = new C(); + I3 i3 = null; + _ = c + i3; + """; + + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "1").VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void BinaryOperators_DifferentType(bool useMetadataReference) + { + var source = @" + using System.Runtime.CompilerServices; + + public class C1 + { + [OverloadResolutionPriority(1)] + public static C1 operator +(C1 c1, C2 c2) => throw null; + } + + public class C2 + { + public static C2 operator +(C1 c1, C2 c2) => throw null; + } + "; + + var executable = @" + var c1 = new C1(); + var c2 = new C2(); + _ = c1 + c2; + "; + + var expectedDiagnostics = new[] { + // (4,17): error CS0034: Operator '+' is ambiguous on operands of type 'C1' and 'C2' + // _ = c1 + c2; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "c1 + c2").WithArguments("+", "C1", "C2").WithLocation(4, 17) + }; + + CreateCompilation([executable, source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics(expectedDiagnostics); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CreateCompilation(executable, references: [AsReference(comp, useMetadataReference)]).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnaryOperator_Allowed(bool useMetadataReference) + { + var source = """ + using System.Runtime.CompilerServices; + + public struct S + { + public static S? operator!(S? s) => throw null; + + [OverloadResolutionPriority(1)] + public static S operator!(S s) + { + System.Console.Write("1"); + return s; + } + } + """; + + var executable = """ + S? s = new S(); + _ = !s; + """; + + CompileAndVerify([executable, source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "1").VerifyDiagnostics(); + } + + [Theory] + [InlineData("implicit")] + [InlineData("explicit")] + public void ConversionOperators_Disallowed(string operatorType) + { + var source = $$""" + using System.Runtime.CompilerServices; + + public class C + { + [OverloadResolutionPriority(1)] + public static {{operatorType}} operator C(int i) => throw null; + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (5,6): error CS9501: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(5, 6) + ); + } + + [Fact] + public void DisallowedOnStaticCtors() + { + var code = """ + using System.Runtime.CompilerServices; + + public class C + { + [OverloadResolutionPriority(1)] + static C() => throw null; + } + """; + + CreateCompilation([code, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (5,6): error CS9501: Cannot use 'OverloadResolutionPriorityAttribute' on this member. + // [OverloadResolutionPriority(1)] + Diagnostic(ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember, "OverloadResolutionPriority(1)").WithLocation(5, 6) + ); + } + + [Theory, CombinatorialData] + public void InterpolatedStringHandlerPriority(bool useMetadataReference) + { + var handler = """ + using System; + using System.Runtime.CompilerServices; + + [InterpolatedStringHandler] + public struct Handler + { + public Handler(int literalLength, int formattedCount) {} + + [OverloadResolutionPriority(1)] + public void AppendLiteral(string s, int i = 0) => Console.Write(1); + public void AppendLiteral(string s) => throw null; + [OverloadResolutionPriority(1)] + public void AppendFormatted(object o) => Console.Write(2); + public void AppendFormatted(int i) => throw null; + + } + """; + + var executable = """ + Handler h = $"test {1}"; + """; + + var verifier = CompileAndVerify([handler, executable, OverloadResolutionPriorityAttributeDefinition, InterpolatedStringHandlerAttribute], expectedOutput: "12").VerifyDiagnostics(); + + verifier.VerifyIL("", """ + { + // Code size 36 (0x24) + .maxstack 3 + .locals init (Handler V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.5 + IL_0003: ldc.i4.1 + IL_0004: call "Handler..ctor(int, int)" + IL_0009: ldloca.s V_0 + IL_000b: ldstr "test " + IL_0010: ldc.i4.0 + IL_0011: call "void Handler.AppendLiteral(string, int)" + IL_0016: ldloca.s V_0 + IL_0018: ldc.i4.1 + IL_0019: box "int" + IL_001e: call "void Handler.AppendFormatted(object)" + IL_0023: ret + } + """); + + var comp = CreateCompilation([handler, OverloadResolutionPriorityAttributeDefinition, InterpolatedStringHandlerAttribute]); + CompileAndVerify(executable, references: [AsReference(comp, useMetadataReference)], expectedOutput: "12").VerifyDiagnostics(); + } + + [Theory] + [InlineData("[System.Runtime.CompilerServices.OverloadResolutionPriority(1)]", "")] + [InlineData("", "[System.Runtime.CompilerServices.OverloadResolutionPriority(1)]")] + public void PartialMethod(string definitionPriority, string implementationPriority) + { + var definition = $$""" + public partial class C + { + {{definitionPriority}} + public partial void M(object o); + } + """; + + var implementation = $$""" + public partial class C + { + {{implementationPriority}} + public partial void M(object o) => System.Console.Write(1); + public void M(string s) => throw null; + } + """; + + var executable = """ + var c = new C(); + c.M(""); + """; + + CompileAndVerify([executable, definition, implementation, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + } + + [Theory] + [InlineData("[System.Runtime.CompilerServices.OverloadResolutionPriority(1)]", "")] + [InlineData("", "[System.Runtime.CompilerServices.OverloadResolutionPriority(1)]")] + public void PartialIndexer(string definitionPriority, string implementationPriority) + { + var definition = $$""" + public partial class C + { + {{definitionPriority}} + public partial int this[object o] { get; set; } + } + """; + + var implementation = $$""" + public partial class C + { + {{implementationPriority}} + public partial int this[object o] + { + get { System.Console.Write(1); return 1; } + set => System.Console.Write(2); + } + public int this[string s] + { + get => throw null; + set => throw null; + } + } + """; + + var executable = """ + var c = new C(); + _ = c[""]; + c[""] = 0; + """; + + CompileAndVerify([executable, definition, implementation, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "12").VerifyDiagnostics(); + } + + [Fact] + public void AttributeAppliedTwiceMethod_Source() + { + var source = """ + using System.Runtime.CompilerServices; + + var c = new C(); + c.M(""); + + public class C + { + [OverloadResolutionPriority(1)] + [OverloadResolutionPriority(2)] + public void M(object o) => System.Console.Write(1); + public void M(string s) => System.Console.Write(2); + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (9,6): error CS0579: Duplicate 'OverloadResolutionPriority' attribute + // [OverloadResolutionPriority(2)] + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "OverloadResolutionPriority").WithArguments("OverloadResolutionPriority").WithLocation(9, 6) + ); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol = model.GetSymbolInfo(invocation).Symbol; + + Assert.Equal("void C.M(System.Object o)", symbol.ToTestDisplayString()); + var underlyingSymbol = symbol.GetSymbol(); + + Assert.Equal(2, underlyingSymbol!.OverloadResolutionPriority); + } + + [Fact] + public void AttributeAppliedTwiceMethod_Metadata() + { + // Equivalent to: + // public class C + // { + // [OverloadResolutionPriority(1)] + // [OverloadResolutionPriority(2)] + // public void M(object o) => System.Console.Write(1); + // public void M(string s) => System.Console.Write(2); + // } + var il = """ + .class public auto ansi beforefieldinit C extends [mscorlib]System.Object + { + .method public hidebysig + instance void M ( + object o + ) cil managed + { + .custom instance void System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 01 00 00 00 00 00 + ) + .custom instance void System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 02 00 00 00 00 00 + ) + ldc.i4.1 + call void [mscorlib]System.Console::Write(int32) + ret + } // end of method C::M + + .method public hidebysig + instance void M ( + string s + ) cil managed + { + ldc.i4.2 + call void [mscorlib]System.Console::Write(int32) + ret + } // end of method C::M + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } // end of method C::.ctor + } // end of class C + + """; + + var ilRef = CompileIL(il + OverloadResolutionPriorityAttributeILDefinition); + + var source = """ + var c = new C(); + c.M(""); + """; + + var comp = CreateCompilation(source, references: [ilRef]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol = model.GetSymbolInfo(invocation).Symbol; + + Assert.Equal("void C.M(System.Object o)", symbol.ToTestDisplayString()); + var underlyingSymbol = symbol.GetSymbol(); + + Assert.Equal(2, underlyingSymbol!.OverloadResolutionPriority); + } + + [Fact] + public void AttributeAppliedTwiceConstructor_Source() + { + var source = """ + using System.Runtime.CompilerServices; + + var c = new C(""); + + public class C + { + [OverloadResolutionPriority(1)] + [OverloadResolutionPriority(2)] + public C(object o) {} + public C(string s) {} + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (8,6): error CS0579: Duplicate 'OverloadResolutionPriority' attribute + // [OverloadResolutionPriority(2)] + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "OverloadResolutionPriority").WithArguments("OverloadResolutionPriority").WithLocation(8, 6) + ); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol = model.GetSymbolInfo(invocation).Symbol; + + Assert.Equal("C..ctor(System.Object o)", symbol.ToTestDisplayString()); + var underlyingSymbol = symbol.GetSymbol(); + + Assert.Equal(2, underlyingSymbol!.OverloadResolutionPriority); + } + + [Fact] + public void AttributeAppliedTwiceConstructor_Metadata() + { + // Equivalent to: + // public class C + // { + // [OverloadResolutionPriority(1)] + // [OverloadResolutionPriority(2)] + // public C(object o) {} + // public C(string s) {} + // } + var il = """ + .class public auto ansi beforefieldinit C extends [mscorlib]System.Object + { + .method public hidebysig specialname rtspecialname + instance void .ctor ( + object o + ) cil managed + { + .custom instance void System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 01 00 00 00 00 00 + ) + .custom instance void System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 02 00 00 00 00 00 + ) + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor ( + string s + ) cil managed + { + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } // end of method C::.ctor + } // end of class C + + """; + + var ilRef = CompileIL(il + OverloadResolutionPriorityAttributeILDefinition); + + var source = """ + var c = new C(""); + """; + + var comp = CreateCompilation(source, references: [ilRef]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol = model.GetSymbolInfo(invocation).Symbol; + + Assert.Equal("C..ctor(System.Object o)", symbol.ToTestDisplayString()); + var underlyingSymbol = symbol.GetSymbol(); + + Assert.Equal(2, underlyingSymbol!.OverloadResolutionPriority); + } + + [Fact] + public void AttributeAppliedTwiceIndexer_Source() + { + var source = """ + using System.Runtime.CompilerServices; + + var c = new C(); + _ = c[""]; + c[""] = 0; + + public class C + { + [OverloadResolutionPriority(1)] + [OverloadResolutionPriority(2)] + public int this[object o] + { + get => throw null; + set => throw null; + } + public int this[string o] + { + get => throw null; + set => throw null; + } + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (10,6): error CS0579: Duplicate 'OverloadResolutionPriority' attribute + // [OverloadResolutionPriority(2)] + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "OverloadResolutionPriority").WithArguments("OverloadResolutionPriority").WithLocation(10, 6) + ); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var symbols = tree.GetRoot().DescendantNodes() + .OfType() + .Select(i => model.GetSymbolInfo(i).Symbol) + .ToArray(); + + Assert.Equal(2, symbols.Length); + + Assert.All(symbols, s => + { + AssertEx.Equal("System.Int32 C.this[System.Object o] { get; set; }", s.ToTestDisplayString()); + var underlyingSymbol = s.GetSymbol(); + + Assert.Equal(2, underlyingSymbol!.OverloadResolutionPriority); + }); + } + + [Fact] + public void AttributeAppliedTwiceIndexer_Metadata() + { + // Equivalent to: + // public class C + // { + // [OverloadResolutionPriority(1)] + // [OverloadResolutionPriority(2)] + // public int this[object o] + // { + // get => throw null; + // set => throw null; + // } + // public int this[string o] + // { + // get => throw null; + // set => throw null; + // } + // } + var il = """ + .class public auto ansi beforefieldinit C extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + // Methods + .method public hidebysig specialname + instance int32 get_Item ( + object o + ) cil managed + { + ldnull + throw + } // end of method C::get_Item + + .method public hidebysig specialname + instance void set_Item ( + object o, + int32 'value' + ) cil managed + { + ldnull + throw + } // end of method C::set_Item + + .method public hidebysig specialname + instance int32 get_Item ( + string o + ) cil managed + { + ldnull + throw + } // end of method C::get_Item + + .method public hidebysig specialname + instance void set_Item ( + string o, + int32 'value' + ) cil managed + { + ldnull + throw + } // end of method C::set_Item + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } // end of method C::.ctor + + // Properties + .property instance int32 Item( + object o + ) + { + .custom instance void System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 01 00 00 00 00 00 + ) + .custom instance void System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::.ctor(int32) = ( + 01 00 02 00 00 00 00 00 + ) + .get instance int32 C::get_Item(object) + .set instance void C::set_Item(object, int32) + } + .property instance int32 Item( + string o + ) + { + .get instance int32 C::get_Item(string) + .set instance void C::set_Item(string, int32) + } + + } // end of class C + + """; + + var ilRef = CompileIL(il + OverloadResolutionPriorityAttributeILDefinition); + + var source = """ + var c = new C(); + _ = c[""]; + c[""] = 0; + """; + + var comp = CreateCompilation(source, references: [ilRef]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var symbols = tree.GetRoot().DescendantNodes() + .OfType() + .Select(i => model.GetSymbolInfo(i).Symbol) + .ToArray(); + + Assert.Equal(2, symbols.Length); + + Assert.All(symbols, s => + { + AssertEx.Equal("System.Int32 C.this[System.Object o] { get; set; }", s.ToTestDisplayString()); + var underlyingSymbol = s.GetSymbol(); + + Assert.Equal(2, underlyingSymbol!.OverloadResolutionPriority); + }); + } + + [Fact] + public void HonoredInsideExpressionTree() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + using System.Runtime.CompilerServices; + + Expression e = () => C.M(1, 2, 3); + + public class C + { + public static void M(params int[] a) + { + } + + [OverloadResolutionPriority(1)] + public static void M(params IEnumerable e) + { + } + } + """; + + CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics( + // (6,30): error CS9226: An expression tree may not contain an expanded form of non-array params collection parameter. + // Expression e = () => C.M(1, 2, 3); + Diagnostic(ErrorCode.ERR_ParamsCollectionExpressionTree, "C.M(1, 2, 3)").WithLocation(6, 30) + ); + } + + [Fact] + public void QuerySyntax() + { + var source = """ + using System; + using System.Runtime.CompilerServices; + + var c = new C(); + _ = from x in c select x; + + class C + { + [OverloadResolutionPriority(1)] + public C Select(Func selector, int i = 0) { Console.Write(1); return this; } + public C Select(Func selector) => throw null; + } + """; + + CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1").VerifyDiagnostics(); + } + + [Fact] + public void ObjectInitializers() + { + var source = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + class C : IEnumerable + { + private List _list = new(); + public void Add(int x) { throw null; } + [OverloadResolutionPriority(1)] public void Add(int x, int y = 0) { _list.Add(x); } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => null; + + [OverloadResolutionPriority(1)] + public int this[int i, int y = 0] + { + set { _list.Add(i); _list.Add(value); } + } + + public int this[int i] + { + set => throw null; + } + } + + class Program + { + static void Main() + { + C c = new() { 1 }; + foreach (var i in c) Console.Write(i); + c = [2]; + foreach (var i in c) Console.Write(i); + c = new() { [3] = 4 }; + foreach (var i in c) Console.Write(i); + } + } + """; + + CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1234"); + } +} diff --git a/src/Compilers/Core/Portable/InternalUtilities/OneOrMany.cs b/src/Compilers/Core/Portable/InternalUtilities/OneOrMany.cs index 0ba8355515f24..10856414dfe81 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/OneOrMany.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/OneOrMany.cs @@ -94,6 +94,18 @@ public OneOrMany Add(T item) IsEmpty ? OneOrMany.Create(item) : OneOrMany.Create(_many.Add(item)); + public void AddRangeTo(ArrayBuilder builder) + { + if (HasOneItem) + { + builder.Add(_one); + } + else + { + builder.AddRange(_many); + } + } + public bool Contains(T item) => HasOneItem ? EqualityComparer.Default.Equals(item, _one) : _many.Contains(item); diff --git a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs index 6ef99b4a6cb19..53d2b58105ff2 100644 --- a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs +++ b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs @@ -3229,6 +3229,20 @@ internal bool HasNullableAttribute(EntityHandle token, out byte defaultTransform return TryExtractByteArrayValueFromAttribute(info.Handle, out nullableTransforms); } + internal bool TryGetOverloadResolutionPriorityValue(EntityHandle token, out int decodedPriority) + { + AttributeInfo info = FindTargetAttribute(token, AttributeDescription.OverloadResolutionPriorityAttribute); + Debug.Assert(!info.HasValue || info.SignatureIndex == 0); + + if (!info.HasValue) + { + decodedPriority = 0; + return false; + } + + return TryExtractValueFromAttribute(info.Handle, out decodedPriority, s_attributeIntValueExtractor); + } + #endregion #region TypeSpec helpers diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index cf440cccf3c85..7a6618dcf3f05 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -489,5 +489,6 @@ static AttributeDescription() internal static readonly AttributeDescription UnscopedRefAttribute = new AttributeDescription("System.Diagnostics.CodeAnalysis", "UnscopedRefAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription InlineArrayAttribute = new AttributeDescription("System.Runtime.CompilerServices", "InlineArrayAttribute", s_signatures_HasThis_Void_Int32_Only); internal static readonly AttributeDescription CollectionBuilderAttribute = new AttributeDescription("System.Runtime.CompilerServices", "CollectionBuilderAttribute", s_signaturesOfCollectionBuilderAttribute); + internal static readonly AttributeDescription OverloadResolutionPriorityAttribute = new AttributeDescription("System.Runtime.CompilerServices", "OverloadResolutionPriorityAttribute", s_signatures_HasThis_Void_Int32_Only); } } diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs index 08868f0bd94ad..eaf500643a99b 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CodeAnalysis { @@ -73,5 +74,25 @@ public bool HasSetsRequiredMembersAttribute } } #endregion + + #region OverloadResolutionPriorityAttribute + private int? _overloadResolutionPriority = null; + [DisallowNull] + public int? OverloadResolutionPriority + { + get + { + VerifySealed(expected: true); + return _overloadResolutionPriority; + } + set + { + VerifySealed(expected: false); + Debug.Assert(value != null); + _overloadResolutionPriority = value; + SetDataStored(); + } + } + #endregion } } diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs index 523ba1225f2e7..5582c14667ef9 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Diagnostics; -using Microsoft.CodeAnalysis.Text; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CodeAnalysis { @@ -16,7 +14,8 @@ internal class CommonPropertyEarlyWellKnownAttributeData : EarlyWellKnownAttribu { #region ObsoleteAttribute private ObsoleteAttributeData _obsoleteAttributeData = ObsoleteAttributeData.Uninitialized; - public ObsoleteAttributeData ObsoleteAttributeData + [DisallowNull] + public ObsoleteAttributeData? ObsoleteAttributeData { get { @@ -37,5 +36,25 @@ public ObsoleteAttributeData ObsoleteAttributeData } } #endregion + + #region OverloadResolutionPriorityAttribute + private int? _overloadResolutionPriority = null; + [DisallowNull] + public int? OverloadResolutionPriority + { + get + { + VerifySealed(expected: true); + return _overloadResolutionPriority; + } + set + { + VerifySealed(expected: false); + Debug.Assert(value != null); + _overloadResolutionPriority = value; + SetDataStored(); + } + } + #endregion } } diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 2c22b59dac227..45791aa0b336c 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -716,6 +716,55 @@ public CollectionBuilderAttribute(Type builderType, string methodName) { } } """; + internal const string OverloadResolutionPriorityAttributeDefinition = """ + namespace System.Runtime.CompilerServices; + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + public sealed class OverloadResolutionPriorityAttribute(int priority) : Attribute + { + public int Priority => priority; + } + """; + + internal const string OverloadResolutionPriorityAttributeILDefinition = """ + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute + extends [mscorlib]System.Attribute + { + .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( + 01 00 e0 00 00 00 02 00 54 02 0d 41 6c 6c 6f 77 + 4d 75 6c 74 69 70 6c 65 00 54 02 09 49 6e 68 65 + 72 69 74 65 64 00 + ) + .field private int32 'P' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + .method public hidebysig specialname rtspecialname + instance void .ctor ( + int32 priority + ) cil managed + { + ldarg.0 + ldarg.1 + stfld int32 System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::'P' + ldarg.0 + call instance void [mscorlib]System.Attribute::.ctor() + ret + } + .method public hidebysig specialname + instance int32 get_Priority () cil managed + { + ldarg.0 + ldfld int32 System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::'P' + ret + } + .property instance int32 Priority() + { + .get instance int32 System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute::get_Priority() + } + } + """; + protected static T GetSyntax(SyntaxTree tree, string text) { return GetSyntaxes(tree, text).Single(); diff --git a/src/Dependencies/PooledObjects/ArrayBuilder.cs b/src/Dependencies/PooledObjects/ArrayBuilder.cs index b1dce5fbdf91e..c76055bead00d 100644 --- a/src/Dependencies/PooledObjects/ArrayBuilder.cs +++ b/src/Dependencies/PooledObjects/ArrayBuilder.cs @@ -634,6 +634,13 @@ public void AddRange(T[] items, int length) _builder.AddRange(items, length); } +#if COMPILERCORE + public void AddRange(OneOrMany items) + { + items.AddRangeTo(this); + } +#endif + public void Clip(int limit) { Debug.Assert(limit <= Count); diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs index 37b022abd3dd3..184fcf547c870 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs @@ -764,5 +764,7 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil builderArgument = null; return false; } + + internal override int? TryGetOverloadResolutionPriority() => null; } } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs index 7703a521de2e0..b965996ffd955 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs @@ -285,6 +285,8 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } + internal override int? TryGetOverloadResolutionPriority() => null; + #if DEBUG protected override MethodSymbolAdapter CreateCciAdapter() {