Skip to content

Commit f95c92c

Browse files
author
Julien Couvreur
authored
Extensions: pattern-based constructs (#78480)
1 parent 98d30b2 commit f95c92c

29 files changed

+4725
-1276
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ private BoundExpression MakeDeconstructInvocationExpression(
683683
// This prevents, for example, an unused params parameter after the out parameters.
684684
var deconstructMethod = ((BoundCall)result).Method;
685685
var parameters = deconstructMethod.Parameters;
686-
for (int i = (deconstructMethod.IsExtensionMethod ? 1 : 0); i < parameters.Length; i++) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions
686+
for (int i = (deconstructMethod.IsExtensionMethod ? 1 : 0); i < parameters.Length; i++)
687687
{
688688
if (parameters[i].RefKind != RefKind.Out)
689689
{

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ private BoundInterpolatedString BindUnconvertedInterpolatedExpressionToFactory(
339339
SyntaxNode syntax = unconvertedSource.Syntax;
340340
ImmutableArray<BoundExpression> expressions = makeInterpolatedStringFactoryArguments(syntax, parts, diagnostics);
341341

342-
BoundExpression construction = MakeInvocationExpression(
342+
BoundExpression construction = MakeInvocationExpression( // Tracked by https://github.com/dotnet/roslyn/issues/76130 : test this scenario with a delegate-returning property (should be blocked by virtue of allowFieldsAndProperties: false)
343343
syntax,
344344
new BoundTypeExpression(syntax, null, factoryType) { WasCompilerGenerated = true },
345345
factoryMethod,

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,11 @@ internal BoundExpression MakeInvocationExpression(
137137
}
138138

139139
BoundExpression result = BindInvocationExpression(
140-
node, node, methodName, boundExpression, analyzedArguments, diagnostics, queryClause,
141-
ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter,
140+
node, node, methodName, boundExpression, analyzedArguments, diagnostics, acceptOnlyMethods: !allowFieldsAndProperties,
141+
queryClause, ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter,
142142
disallowExpandedNonArrayParams: disallowExpandedNonArrayParams);
143143

144-
// Query operator can't be called dynamically.
144+
// Query operator can't be called dynamically.
145145
if (queryClause != null && result.Kind == BoundKind.DynamicInvocation)
146146
{
147147
// the error has already been reported by BindInvocationExpression
@@ -245,7 +245,7 @@ BoundExpression bindArgumentsAndInvocation(InvocationExpressionSyntax node, Boun
245245
boundExpression = CheckValue(boundExpression, BindValueKind.RValueOrMethodGroup, diagnostics);
246246
string name = boundExpression.Kind == BoundKind.MethodGroup ? GetName(node.Expression) : null;
247247
BindArgumentsAndNames(node.ArgumentList, diagnostics, analyzedArguments, allowArglist: true);
248-
return BindInvocationExpression(node, node.Expression, name, boundExpression, analyzedArguments, diagnostics);
248+
return BindInvocationExpression(node, node.Expression, name, boundExpression, analyzedArguments, diagnostics, acceptOnlyMethods: false);
249249
}
250250

251251
static bool receiverIsInvocation(InvocationExpressionSyntax node, out InvocationExpressionSyntax nested)
@@ -324,6 +324,7 @@ private BoundExpression BindInvocationExpression(
324324
BoundExpression boundExpression,
325325
AnalyzedArguments analyzedArguments,
326326
BindingDiagnosticBag diagnostics,
327+
bool acceptOnlyMethods,
327328
CSharpSyntaxNode queryClause = null,
328329
bool ignoreNormalFormIfHasValidParamsParameter = false,
329330
bool disallowExpandedNonArrayParams = false)
@@ -356,7 +357,7 @@ private BoundExpression BindInvocationExpression(
356357
ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter,
357358
disallowExpandedNonArrayParams: disallowExpandedNonArrayParams,
358359
anyApplicableCandidates: out _,
359-
acceptOnlyMethods: false);
360+
acceptOnlyMethods: acceptOnlyMethods);
360361
}
361362
else if ((object)(delegateType = GetDelegateType(boundExpression)) != null)
362363
{
@@ -727,7 +728,7 @@ private BoundExpression BindMethodGroupInvocation(
727728
Debug.Assert(extensionMemberAccess.Kind != BoundKind.MethodGroup);
728729

729730
extensionMemberAccess = CheckValue(extensionMemberAccess, BindValueKind.RValue, diagnostics);
730-
BoundExpression extensionMemberInvocation = BindInvocationExpression(syntax, expression, methodName: null, extensionMemberAccess, analyzedArguments, diagnostics);
731+
BoundExpression extensionMemberInvocation = BindInvocationExpression(syntax, expression, methodName: null, extensionMemberAccess, analyzedArguments, diagnostics, acceptOnlyMethods: false);
731732
anyApplicableCandidates = !extensionMemberInvocation.HasAnyErrors;
732733
return extensionMemberInvocation;
733734
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ deconstructMethod is null &&
10581058
if (deconstructMethod is null)
10591059
hasErrors = true;
10601060

1061-
int skippedExtensionParameters = deconstructMethod?.IsExtensionMethod == true ? 1 : 0; // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions
1061+
int skippedExtensionParameters = deconstructMethod?.IsExtensionMethod == true ? 1 : 0;
10621062
for (int i = 0; i < node.Subpatterns.Count; i++)
10631063
{
10641064
var subPattern = node.Subpatterns[i];

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ static SafeContext getDeclarationValEscape(BoundTypeExpression typeExpression, S
627627
// and so the ref safety of the pattern is equivalent to a `Deconstruct(out var ...)` invocation
628628
// where "safe-context inference of declaration expressions" would have the same effect.
629629
if (node.DeconstructMethod is { } m &&
630-
tryGetThisParameter(m)?.EffectiveScope == ScopedKind.None)
630+
tryGetReceiverParameter(m)?.EffectiveScope == ScopedKind.None)
631631
{
632632
using (new PatternInput(this, _localScopeDepth))
633633
{
@@ -637,12 +637,16 @@ static SafeContext getDeclarationValEscape(BoundTypeExpression typeExpression, S
637637

638638
return base.VisitRecursivePattern(node);
639639

640-
static ParameterSymbol? tryGetThisParameter(MethodSymbol method)
640+
static ParameterSymbol? tryGetReceiverParameter(MethodSymbol method)
641641
{
642-
if (method.IsExtensionMethod) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions
642+
if (method.IsExtensionMethod)
643643
{
644644
return method.Parameters is [{ } firstParameter, ..] ? firstParameter : null;
645645
}
646+
else if (method.GetIsNewExtensionMember())
647+
{
648+
return method.ContainingType.ExtensionParameter;
649+
}
646650

647651
return method.TryGetThisParameter(out var thisParameter) ? thisParameter : null;
648652
}

src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression
652652
}
653653
else if (IsUnfixedTypeParameter(target) && !target.NullableAnnotation.IsAnnotated() && kind is ExactOrBoundsKind.LowerBound)
654654
{
655-
var ordinal = GetOrdinal((TypeParameterSymbol)target.Type); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : test nullability scenario where the override of ordinals matters
655+
var ordinal = GetOrdinal((TypeParameterSymbol)target.Type);
656656
_nullableAnnotationLowerBounds[ordinal] = _nullableAnnotationLowerBounds[ordinal].Join(argumentType.NullableAnnotation);
657657
}
658658
}

src/Compilers/CSharp/Portable/CSharpResources.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@
547547
<value>Inconsistent accessibility: indexer return type '{1}' is less accessible than indexer '{0}'</value>
548548
</data>
549549
<data name="ERR_BadVisIndexerParam" xml:space="preserve">
550-
<value>Inconsistent accessibility: parameter type '{1}' is less accessible than indexer '{0}'</value>
550+
<value>Inconsistent accessibility: parameter type '{1}' is less accessible than indexer or property '{0}'</value>
551551
</data>
552552
<data name="ERR_BadVisOpReturn" xml:space="preserve">
553553
<value>Inconsistent accessibility: return type '{1}' is less accessible than operator '{0}'</value>

src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ protected BoundExpression LowerEvaluation(BoundDagEvaluation evaluation)
152152
PropertySymbol property = p.Property;
153153
var outputTemp = new BoundDagTemp(p.Syntax, property.Type, p);
154154
BoundExpression output = _tempAllocator.GetTemp(outputTemp);
155+
input = _factory.ConvertReceiverForExtensionMemberIfNeeded(property, input);
155156
return _factory.AssignmentExpression(output, _localRewriter.MakePropertyAccess(_factory.Syntax, input, property, LookupResultKind.Viable, property.Type, isLeftOfAssignment: false));
156157
}
157158

@@ -169,7 +170,7 @@ void addArg(RefKind refKind, BoundExpression expression)
169170

170171
Debug.Assert(method.Name == WellKnownMemberNames.DeconstructMethodName);
171172
int extensionExtra;
172-
if (method.IsStatic) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions
173+
if (method.IsStatic)
173174
{
174175
Debug.Assert(method.IsExtensionMethod);
175176
receiver = _factory.Type(method.ContainingType);
@@ -190,6 +191,7 @@ void addArg(RefKind refKind, BoundExpression expression)
190191
addArg(RefKind.Out, _tempAllocator.GetTemp(outputTemp));
191192
}
192193

194+
receiver = _factory.ConvertReceiverForExtensionMemberIfNeeded(method, receiver);
193195
return _factory.Call(receiver, method, refKindBuilder.ToImmutableAndFree(), argBuilder.ToImmutableAndFree());
194196
}
195197

src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ private BoundStatement InitializeFixedStatementGetPinnable(
355355
}
356356

357357
// .GetPinnable()
358+
callReceiver = factory.ConvertReceiverForExtensionMemberIfNeeded(getPinnableMethod, callReceiver);
358359
var getPinnableCall = getPinnableMethod.IsStatic ?
359360
factory.Call(null, getPinnableMethod, callReceiver) :
360361
factory.Call(callReceiver, getPinnableMethod);

src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,9 +698,11 @@ private BoundExpression MakeObjectInitializerMemberAccess(
698698
#if DEBUG
699699
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
700700
Debug.Assert(_compilation.Conversions.ClassifyConversionFromType(rewrittenReceiver.Type, memberSymbol.ContainingType, isChecked: false, ref discardedUseSiteInfo).IsImplicit ||
701+
(memberSymbol.GetIsNewExtensionMember() && !memberSymbol.IsStatic && ConversionsBase.IsValidExtensionMethodThisArgConversion(_compilation.Conversions.ClassifyConversionFromType(rewrittenReceiver.Type, memberSymbol.ContainingType.ExtensionParameter!.Type, isChecked: false, ref discardedUseSiteInfo))) ||
701702
_compilation.Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(rewrittenReceiver.Type, memberSymbol.ContainingType, ref discardedUseSiteInfo, out _));
702703
// It is possible there are use site diagnostics from the above, but none that we need report as we aren't generating code for the conversion
703704
#endif
705+
rewrittenReceiver = _factory.ConvertReceiverForExtensionMemberIfNeeded(memberSymbol, rewrittenReceiver);
704706

705707
switch (memberSymbol.Kind)
706708
{

0 commit comments

Comments
 (0)