Skip to content

Commit 223609d

Browse files
author
Julien Couvreur
committed
Extensions: bind unconditionally of LangVer
1 parent d273205 commit 223609d

File tree

9 files changed

+283
-159
lines changed

9 files changed

+283
-159
lines changed

src/Compilers/CSharp/Portable/Binder/Binder.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,19 @@ internal static void ReportDiagnosticsIfObsoleteInternal(BindingDiagnosticBag di
749749
}
750750
}
751751

752+
internal static bool IsDisallowedExtensionInOlderLangVer(MethodSymbol symbol)
753+
{
754+
return symbol.GetIsNewExtensionMember() && symbol.IsStatic;
755+
}
756+
757+
internal static void ReportDiagnosticsIfDisallowedExtension(BindingDiagnosticBag diagnostics, MethodSymbol method, SyntaxNode syntax)
758+
{
759+
if (IsDisallowedExtensionInOlderLangVer(method))
760+
{
761+
MessageID.IDS_FeatureExtensions.CheckFeatureAvailability(diagnostics, syntax);
762+
}
763+
}
764+
752765
internal static void ReportDiagnosticsIfUnmanagedCallersOnly(BindingDiagnosticBag diagnostics, MethodSymbol symbol, SyntaxNodeOrToken syntax, bool isDelegateConversion)
753766
{
754767
var unmanagedCallersOnlyAttributeData = symbol.GetUnmanagedCallersOnlyAttributeData(forceComplete: false);

src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs

Lines changed: 12 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,7 @@ private bool HasCollectionInitializerTypeInProgress(SyntaxNode syntax, TypeSymbo
10691069
ReportDiagnosticsIfObsolete(diagnostics, collectionBuilderMethod.ContainingType, syntax, hasBaseReceiver: false);
10701070
ReportDiagnosticsIfObsolete(diagnostics, collectionBuilderMethod, syntax, hasBaseReceiver: false);
10711071
ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, collectionBuilderMethod, syntax, isDelegateConversion: false);
1072+
Debug.Assert(!collectionBuilderMethod.GetIsNewExtensionMember());
10721073

10731074
return collectionBuilderMethod;
10741075
}
@@ -1430,15 +1431,21 @@ static bool bindMethodGroupInvocation(
14301431
{
14311432
addMethodBinder.ReportDiagnosticsIfObsolete(diagnostics, addMethods[0], syntax, hasBaseReceiver: false);
14321433
ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, addMethods[0], syntax, isDelegateConversion: false);
1434+
Debug.Assert(!IsDisallowedExtensionInOlderLangVer(addMethods[0]));
14331435
}
14341436
}
14351437
}
14361438
else
14371439
{
1438-
result = bindInvocationExpressionContinued(
1439-
addMethodBinder, syntax, expression, resolution.OverloadResolutionResult, resolution.AnalyzedArguments,
1440-
resolution.MethodGroup, diagnostics: diagnostics, out var addMethod);
1441-
addMethods = addMethod is null ? [] : [addMethod];
1440+
Debug.Assert(!resolution.OverloadResolutionResult.Succeeded);
1441+
1442+
resolution.OverloadResolutionResult.ReportDiagnostics(
1443+
binder: addMethodBinder, location: GetLocationForOverloadResolutionDiagnostic(syntax, expression), nodeOpt: syntax, diagnostics: diagnostics, name: WellKnownMemberNames.CollectionInitializerAddMethodName,
1444+
receiver: resolution.MethodGroup.Receiver, invokedExpression: expression, arguments: analyzedArguments, memberGroup: resolution.MethodGroup.Methods.ToImmutable(),
1445+
typeContainingConstructor: null, delegateTypeBeingInvoked: null, queryClause: null);
1446+
1447+
addMethods = [];
1448+
result = false;
14421449
}
14431450
}
14441451
}
@@ -1554,83 +1561,6 @@ static ImmutableArray<MethodSymbol> filterOutBadGenericMethods(
15541561

15551562
return resultBuilder.ToImmutableAndFree();
15561563
}
1557-
1558-
// This is what BindInvocationExpressionContinued is doing in terms of reporting diagnostics and detecting a failure
1559-
static bool bindInvocationExpressionContinued(
1560-
Binder addMethodBinder,
1561-
SyntaxNode node,
1562-
SyntaxNode expression,
1563-
OverloadResolutionResult<MethodSymbol> result,
1564-
AnalyzedArguments analyzedArguments,
1565-
MethodGroup methodGroup,
1566-
BindingDiagnosticBag diagnostics,
1567-
out MethodSymbol? addMethod)
1568-
{
1569-
Debug.Assert(node != null);
1570-
Debug.Assert(methodGroup != null);
1571-
Debug.Assert(methodGroup.Error == null);
1572-
Debug.Assert(methodGroup.Methods.Count > 0);
1573-
1574-
var invokedAsExtensionMethod = methodGroup.IsExtensionMethodGroup;
1575-
1576-
// We have already determined that we are not in a situation where we can successfully do
1577-
// a dynamic binding. We might be in one of the following situations:
1578-
//
1579-
// * There were dynamic arguments but overload resolution still found zero applicable candidates.
1580-
// * There were no dynamic arguments and overload resolution found zero applicable candidates.
1581-
// * There were no dynamic arguments and overload resolution found multiple applicable candidates
1582-
// without being able to find the best one.
1583-
//
1584-
// In those three situations we might give an additional error.
1585-
1586-
if (!result.Succeeded)
1587-
{
1588-
// Since there were no argument errors to report, we report an error on the invocation itself.
1589-
result.ReportDiagnostics(
1590-
binder: addMethodBinder, location: GetLocationForOverloadResolutionDiagnostic(node, expression), nodeOpt: node, diagnostics: diagnostics, name: WellKnownMemberNames.CollectionInitializerAddMethodName,
1591-
receiver: methodGroup.Receiver, invokedExpression: expression, arguments: analyzedArguments, memberGroup: methodGroup.Methods.ToImmutable(),
1592-
typeContainingConstructor: null, delegateTypeBeingInvoked: null, queryClause: null);
1593-
1594-
addMethod = null;
1595-
return false;
1596-
}
1597-
1598-
// Otherwise, there were no dynamic arguments and overload resolution found a unique best candidate.
1599-
// We still have to determine if it passes final validation.
1600-
1601-
var methodResult = result.ValidResult;
1602-
var method = methodResult.Member;
1603-
1604-
// Tracked by https://github.com/dotnet/roslyn/issues/76130: It looks like we added a bunch of code in BindInvocationExpressionContinued at this position
1605-
// that specifically deals with new extension methods. It adjusts analyzedArguments, etc.
1606-
// It is very likely we need to do the same here.
1607-
1608-
// It is possible that overload resolution succeeded, but we have chosen an
1609-
// instance method and we're in a static method. A careful reading of the
1610-
// overload resolution spec shows that the "final validation" stage allows an
1611-
// "implicit this" on any method call, not just method calls from inside
1612-
// instance methods. Therefore we must detect this scenario here, rather than in
1613-
// overload resolution.
1614-
1615-
var receiver = methodGroup.Receiver;
1616-
1617-
// Note: we specifically want to do final validation (7.6.5.1) without checking delegate compatibility (15.2),
1618-
// so we're calling MethodGroupFinalValidation directly, rather than via MethodGroupConversionHasErrors.
1619-
// Note: final validation wants the receiver that corresponds to the source representation
1620-
// (i.e. the first argument, if invokedAsExtensionMethod).
1621-
var gotError = addMethodBinder.MemberGroupFinalValidation(receiver, method, expression, diagnostics, invokedAsExtensionMethod);
1622-
1623-
addMethodBinder.ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver: false);
1624-
ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, method, node, isDelegateConversion: false);
1625-
1626-
// No use site errors, but there could be use site warnings.
1627-
// If there are any use site warnings, they have already been reported by overload resolution.
1628-
Debug.Assert(!method.HasUseSiteError, "Shouldn't have reached this point if there were use site errors.");
1629-
Debug.Assert(!method.IsRuntimeFinalizer());
1630-
1631-
addMethod = method;
1632-
return !gotError;
1633-
}
16341564
}
16351565

16361566
/// <summary>
@@ -3051,6 +2981,7 @@ private bool MethodGroupConversionHasErrors(
30512981
ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, selectedMethod, syntax, isDelegateConversion: true);
30522982
}
30532983
ReportDiagnosticsIfObsolete(diagnostics, selectedMethod, syntax, hasBaseReceiver: false);
2984+
ReportDiagnosticsIfDisallowedExtension(diagnostics, selectedMethod, syntax);
30542985

30552986
// No use site errors, but there could be use site warnings.
30562987
// If there are use site warnings, they were reported during the overload resolution process

src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8030,6 +8030,7 @@ private BoundExpression MakeMemberAccessValue(BoundExpression expr, BindingDiagn
80308030

80318031
private BoundExpression GetExtensionMemberAccess(SyntaxNode syntax, BoundExpression? receiver, Symbol extensionMember, BindingDiagnosticBag diagnostics)
80328032
{
8033+
MessageID.IDS_FeatureExtensions.CheckFeatureAvailability(diagnostics, syntax);
80338034
receiver = ReplaceTypeOrValueReceiver(receiver, useType: extensionMember.IsStatic, diagnostics);
80348035

80358036
switch (extensionMember)

src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,7 @@ private BoundCall BindInvocationExpressionContinued(
13251325

13261326
ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver);
13271327
ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, method, node, isDelegateConversion: false);
1328+
ReportDiagnosticsIfDisallowedExtension(diagnostics, method, node);
13281329

13291330
// No use site errors, but there could be use site warnings.
13301331
// If there are any use site warnings, they have already been reported by overload resolution.

src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -202,32 +202,29 @@ internal void EnumerateAllExtensionMembersInSingleBinder(ArrayBuilder<SingleLook
202202
PooledHashSet<MethodSymbol>? implementationsToShadow = null;
203203

204204
// 1. Collect new extension members
205-
if (this.Compilation.LanguageVersion.AllowNewExtensions())
205+
var extensionDeclarations = ArrayBuilder<NamedTypeSymbol>.GetInstance();
206+
this.GetExtensionDeclarations(extensionDeclarations, originalBinder);
207+
208+
foreach (NamedTypeSymbol extensionDeclaration in extensionDeclarations)
206209
{
207-
var extensionDeclarations = ArrayBuilder<NamedTypeSymbol>.GetInstance();
208-
this.GetExtensionDeclarations(extensionDeclarations, originalBinder);
210+
var candidates = name is null ? extensionDeclaration.GetMembers() : extensionDeclaration.GetMembers(name);
209211

210-
foreach (NamedTypeSymbol extensionDeclaration in extensionDeclarations)
212+
foreach (var candidate in candidates)
211213
{
212-
var candidates = name is null ? extensionDeclaration.GetMembers() : extensionDeclaration.GetMembers(name);
214+
SingleLookupResult resultOfThisMember = originalBinder.CheckViability(candidate, arity, options, null, diagnose: true, useSiteInfo: ref useSiteInfo);
215+
result.Add(resultOfThisMember);
213216

214-
foreach (var candidate in candidates)
217+
if (candidate is MethodSymbol { IsStatic: false } shadows &&
218+
shadows.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() is { } toShadow)
215219
{
216-
SingleLookupResult resultOfThisMember = originalBinder.CheckViability(candidate, arity, options, null, diagnose: true, useSiteInfo: ref useSiteInfo);
217-
result.Add(resultOfThisMember);
218-
219-
if (candidate is MethodSymbol { IsStatic: false } shadows &&
220-
shadows.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() is { } toShadow)
221-
{
222-
implementationsToShadow ??= PooledHashSet<MethodSymbol>.GetInstance();
223-
implementationsToShadow.Add(toShadow);
224-
}
220+
implementationsToShadow ??= PooledHashSet<MethodSymbol>.GetInstance();
221+
implementationsToShadow.Add(toShadow);
225222
}
226223
}
227-
228-
extensionDeclarations.Free();
229224
}
230225

226+
extensionDeclarations.Free();
227+
231228
// 2. Collect classic extension methods
232229
var extensionMethods = ArrayBuilder<MethodSymbol>.GetInstance();
233230
this.GetCandidateExtensionMethods(extensionMethods, name, arity, options, originalBinder: originalBinder);

src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ private BoundExpression BindCompoundAssignment(AssignmentExpressionSyntax node,
334334

335335
ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver: false);
336336
ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, method, node, isDelegateConversion: false);
337+
Debug.Assert(!IsDisallowedExtensionInOlderLangVer(method));
337338

338339
inPlaceResult = new BoundCompoundAssignmentOperator(
339340
node,
@@ -2647,6 +2648,7 @@ private BoundExpression BindIncrementOperator(ExpressionSyntax node, ExpressionS
26472648

26482649
ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver: false);
26492650
ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, method, node, isDelegateConversion: false);
2651+
Debug.Assert(!IsDisallowedExtensionInOlderLangVer(method));
26502652

26512653
inPlaceResult = new BoundIncrementOperator(
26522654
node,

src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,12 +465,16 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno
465465
var foreachKeyword = _syntax.ForEachKeyword;
466466
ReportDiagnosticsIfObsolete(diagnostics, getEnumeratorMethod, foreachKeyword, hasBaseReceiver: false);
467467
ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, getEnumeratorMethod, foreachKeyword, isDelegateConversion: false);
468+
Debug.Assert(!IsDisallowedExtensionInOlderLangVer(getEnumeratorMethod));
469+
468470
// MoveNext is an instance method, so it does not need to have unmanaged callers only diagnostics reported.
469471
// Either a diagnostic was reported at the declaration of the method (for the invalid attribute), or MoveNext
470472
// is marked as not supported and we won't get here in the first place (for metadata import).
471473
ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextInfo.Method, foreachKeyword, hasBaseReceiver: false);
472474
ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false);
473475
ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false);
476+
Debug.Assert(!IsDisallowedExtensionInOlderLangVer(builder.MoveNextInfo.Method));
477+
Debug.Assert(!IsDisallowedExtensionInOlderLangVer(builder.CurrentPropertyGetter));
474478

475479
// We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case,
476480
// but it turns out that these are equivalent (when both are available).

src/Compilers/CSharp/Portable/LanguageVersion.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -572,10 +572,5 @@ internal static bool AllowImprovedOverloadCandidates(this LanguageVersion self)
572572
{
573573
return self >= MessageID.IDS_FeatureImprovedOverloadCandidates.RequiredVersion();
574574
}
575-
576-
internal static bool AllowNewExtensions(this LanguageVersion self)
577-
{
578-
return self >= MessageID.IDS_FeatureExtensions.RequiredVersion();
579-
}
580575
}
581576
}

0 commit comments

Comments
 (0)