Skip to content

Commit 78fda9e

Browse files
committed
[ConstraintSystem] Use new fix/diagnostic for name shadowing
Stop filtering outer overload choices while trying to pre-check expression, instead have it always fetch those and use new fix to only attempt them in diagnostic mode (unless it's min/max situation with conditional conformances).
1 parent c9c20af commit 78fda9e

File tree

6 files changed

+77
-86
lines changed

6 files changed

+77
-86
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3273,6 +3273,30 @@ bool ConstraintSystem::repairFailures(
32733273
locator))
32743274
break;
32753275

3276+
{
3277+
auto *calleeLocator = getCalleeLocator(loc);
3278+
if (hasFixFor(calleeLocator, FixKind::AddQualifierToAccessTopLevelName)) {
3279+
if (auto overload = findSelectedOverloadFor(calleeLocator)) {
3280+
if (auto choice = overload->choice.getDeclOrNull()) {
3281+
// If this is an argument of a symetric function/operator let's
3282+
// not fix any position rather than first because we'd just end
3283+
// up with ambiguity instead of reporting an actual problem with
3284+
// mismatched type since each argument can have district bindings.
3285+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(choice)) {
3286+
auto *paramList = AFD->getParameters();
3287+
auto firstParamType = paramList->get(0)->getInterfaceType();
3288+
if (elt.castTo<LocatorPathElt::ApplyArgToParam>().getParamIdx() >
3289+
0 &&
3290+
llvm::all_of(*paramList, [&](const ParamDecl *param) -> bool {
3291+
return param->getInterfaceType()->isEqual(firstParamType);
3292+
}))
3293+
return true;
3294+
}
3295+
}
3296+
}
3297+
}
3298+
}
3299+
32763300
conversionsOrFixes.push_back(
32773301
AllowArgumentMismatch::create(*this, lhs, rhs, loc));
32783302
break;
@@ -6201,8 +6225,27 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
62016225
if (candidates.size() == 1)
62026226
candidates.front()->setFavored();
62036227

6204-
generateConstraints(candidates, memberTy, outerAlternatives,
6205-
useDC, locator);
6228+
// We *might* include any non-members that we found in outer contexts in
6229+
// some special cases, for backwards compatibility: first, we have to be
6230+
// looking for one of the special names ('min' or 'max'), and second, all
6231+
// of the inner (viable) results need to come from conditional
6232+
// conformances. The second condition is how the problem here was
6233+
// encountered: a type ('Range') was made to conditionally conform to a
6234+
// new protocol ('Sequence'), which introduced some extra methods
6235+
// ('min' and 'max') that shadowed global functions that people regularly
6236+
// called within extensions to that type (usually adding 'clamp').
6237+
bool treatAsViable =
6238+
(member.isSimpleName("min") || member.isSimpleName("max")) &&
6239+
allFromConditionalConformances(DC, baseTy, result.ViableCandidates);
6240+
6241+
generateConstraints(
6242+
candidates, memberTy, outerAlternatives, useDC, locator, None,
6243+
/*requiresFix=*/!treatAsViable,
6244+
[&](unsigned, const OverloadChoice &) {
6245+
return treatAsViable ? nullptr
6246+
: AddQualifierToAccessTopLevelName::create(
6247+
*this, locator);
6248+
});
62066249
}
62076250
}
62086251

@@ -8852,6 +8895,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
88528895
case FixKind::AllowInvalidUseOfTrailingClosure:
88538896
case FixKind::AllowNonClassTypeToConvertToAnyObject:
88548897
case FixKind::SpecifyClosureReturnType:
8898+
case FixKind::AddQualifierToAccessTopLevelName:
88558899
llvm_unreachable("handled elsewhere");
88568900
}
88578901

@@ -9315,7 +9359,12 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
93159359

93169360
case ConstraintKind::BindOverload:
93179361
if (auto *fix = constraint.getFix()) {
9318-
if (recordFix(fix))
9362+
// TODO(diagnostics): Impact should be associated with a fix unless
9363+
// it's a contextual problem, then only solver can decide what the impact
9364+
// would be in each particular situation.
9365+
auto impact =
9366+
fix->getKind() == FixKind::AddQualifierToAccessTopLevelName ? 10 : 1;
9367+
if (recordFix(fix, impact))
93199368
return SolutionKind::Error;
93209369
}
93219370

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 10 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -452,20 +452,6 @@ static bool findNonMembers(ArrayRef<LookupResultEntry> lookupResults,
452452
return AllDeclRefs;
453453
}
454454

455-
/// Whether we should be looking at the outer results for a function called \c
456-
/// name.
457-
///
458-
/// This is very restrictive because it's a source compatibility issue (see the
459-
/// if (AllConditionalConformances) { (void)findNonMembers(...); } below).
460-
static bool shouldConsiderOuterResultsFor(DeclNameRef name) {
461-
const StringRef specialNames[] = {"min", "max"};
462-
for (auto specialName : specialNames)
463-
if (name.isSimpleName(specialName))
464-
return true;
465-
466-
return false;
467-
}
468-
469455
/// Bind an UnresolvedDeclRefExpr by performing name lookup and
470456
/// returning the resultant expression. Context is the DeclContext used
471457
/// for the lookup.
@@ -479,8 +465,11 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
479465
NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions;
480466
if (isa<AbstractFunctionDecl>(DC))
481467
lookupOptions |= NameLookupFlags::KnownPrivate;
482-
if (shouldConsiderOuterResultsFor(Name))
483-
lookupOptions |= NameLookupFlags::IncludeOuterResults;
468+
469+
// TODO: Include all of the possible members to give a solver a
470+
// chance to diagnose name shadowing which requires explicit
471+
// name/module qualifier to access top-level name.
472+
lookupOptions |= NameLookupFlags::IncludeOuterResults;
484473

485474
auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions);
486475

@@ -625,14 +614,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
625614
// better matching candidates.
626615
if (localDeclAfterUse) {
627616
auto innerDecl = localDeclAfterUse;
628-
629-
// Perform a thorough lookup if outer results was not included before.
630-
if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) {
631-
auto option = lookupOptions;
632-
option |= NameLookupFlags::IncludeOuterResults;
633-
Lookup = lookupUnqualified(DC, Name, Loc, option);
634-
}
635-
636617
while (localDeclAfterUse) {
637618
if (Lookup.outerResults().empty()) {
638619
Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name);
@@ -649,13 +630,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
649630
findNonMembers(Lookup.innerResults(), UDRE->getRefKind(),
650631
/*breakOnMember=*/true, ResultValues, isValid);
651632
}
652-
653-
// Drop outer results if they are not supposed to be included.
654-
if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) {
655-
Lookup.filter([&](LookupResultEntry Result, bool isOuter) {
656-
return !isOuter;
657-
});
658-
}
659633
}
660634

661635
// If we have an unambiguous reference to a type decl, form a TypeExpr.
@@ -715,7 +689,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
715689

716690
ResultValues.clear();
717691
bool AllMemberRefs = true;
718-
bool AllConditionalConformances = true;
719692
ValueDecl *Base = nullptr;
720693
DeclContext *BaseDC = nullptr;
721694
for (auto Result : Lookup) {
@@ -732,26 +705,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
732705

733706
Base = ThisBase;
734707
BaseDC = Result.getDeclContext();
735-
736-
// Check if this result is derived through a conditional conformance,
737-
// meaning it comes from a protocol (or extension) where there's a
738-
// conditional conformance for the type with the method in question
739-
// (NB. that type may not be the type associated with DC, for tested types
740-
// with static methods).
741-
if (auto Proto = Value->getDeclContext()->getSelfProtocolDecl()) {
742-
auto contextSelfType =
743-
BaseDC->getInnermostTypeContext()->getDeclaredInterfaceType();
744-
auto conformance = conformsToProtocol(
745-
contextSelfType, Proto, DC,
746-
ConformanceCheckFlags::InExpression |
747-
ConformanceCheckFlags::SkipConditionalRequirements);
748-
749-
if (conformance.isInvalid() ||
750-
conformance.getConditionalRequirements().empty()) {
751-
AllConditionalConformances = false;
752-
}
753-
}
754-
755708
continue;
756709
}
757710

@@ -774,22 +727,12 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
774727
/*Implicit=*/true);
775728
}
776729

777-
// We *might* include any non-members that we found in outer contexts in
778-
// some special cases, for backwards compatibility: first, we have to be
779-
// looking for one of the special names
780-
// ('shouldConsiderOuterResultsFor(Name)'), and second, all of the inner
781-
// results need to come from conditional conformances. The second condition
782-
// is how the problem here was encountered: a type ('Range') was made to
783-
// conditionally conform to a new protocol ('Sequence'), which introduced
784-
// some extra methods ('min' and 'max') that shadowed global functions that
785-
// people regularly called within extensions to that type (usually adding
786-
// 'clamp').
787730
llvm::SmallVector<ValueDecl *, 4> outerAlternatives;
788-
if (AllConditionalConformances) {
789-
(void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(),
790-
/*breakOnMember=*/false, outerAlternatives,
791-
/*isValid=*/[&](ValueDecl *) { return true; });
792-
}
731+
(void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(),
732+
/*breakOnMember=*/false, outerAlternatives,
733+
/*isValid=*/[](ValueDecl *choice) -> bool {
734+
return !choice->isInvalid();
735+
});
793736

794737
// Otherwise, form an UnresolvedDotExpr and sema will resolve it based on
795738
// type information.

test/APINotes/versioned-objc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ extension PrintingInterference {
166166
func testDroppingRenamedPrints() {
167167
// CHECK-DIAGS-4: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to instance method
168168
print(self)
169-
// CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: missing argument for parameter 'extra' in call
169+
// CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: use of 'print' refers to instance method rather than global function 'print(_:separator:terminator:)' in module 'Swift'
170170

171171
// CHECK-DIAGS-4-NOT: [[@LINE+1]]:{{[0-9]+}}:
172172
print(self, extra: self)

test/Constraints/members.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ do {
355355
// rdar://problem/25341015
356356
extension Sequence {
357357
func r25341015_1() -> Int {
358-
return max(1, 2) // expected-error {{use of 'max' refers to instance method 'max(by:)' rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}}
358+
return max(1, 2) // expected-error {{use of 'max' refers to instance method rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}}
359359
}
360360
}
361361

@@ -381,7 +381,7 @@ func r25341015() {
381381
class Bar {
382382
func baz() {}
383383
func qux() {
384-
baz(1, 2) // expected-error {{argument passed to call that takes no arguments}}
384+
baz(1, 2) // expected-error {{use of 'baz' refers to instance method rather than local function 'baz'}}
385385
}
386386
}
387387
}
@@ -405,17 +405,17 @@ func bar_32854314() -> Int {
405405
extension Array where Element == Int {
406406
func foo() {
407407
let _ = min(foo_32854314(), bar_32854314()) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}}
408-
// expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}}
408+
// expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}}
409409
}
410410

411411
func foo(_ x: Int, _ y: Double) {
412412
let _ = min(x, y) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}}
413-
// expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}}
413+
// expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}}
414414
}
415415

416416
func bar() {
417417
let _ = min(1.0, 2) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}}
418-
// expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}}
418+
// expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}}
419419
}
420420
}
421421

test/NameBinding/name_lookup_min_max_conditional_conformance.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ extension ContainsMinMax {
1616
func min() {}
1717
}
1818

19-
func foo(_: Int, _: Int) {}
19+
func foo(_: Int, _: Int) {} // expected-note 2 {{'foo' declared here}}
2020

2121
protocol ContainsFoo {}
2222
extension ContainsFoo {
@@ -34,15 +34,14 @@ extension NonConditional {
3434
// expected-error@-1{{use of 'min' refers to instance method}}
3535
// expected-note@-2{{use 'Swift.' to reference the global function}}
3636

37-
// FIXME(diagnostics): Better diagnostic in this case would be to suggest to add `name_lookup_min_max_conditional_conformance.`
38-
// to call because name `foo` is shadowed by instance method without arguments. Would be fixed by `resolveDeclRefExpr` refactoring.
39-
_ = foo(5, 6) // expected-error {{argument passed to call that takes no arguments}}
37+
_ = foo(5, 6) // expected-error {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}}
38+
// expected-note@-1 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}}
4039
}
4140
}
4241

4342
struct Conditional<T> {}
4443
extension Conditional: ContainsMinMax where T: ContainsMinMax {}
45-
extension Conditional: ContainsFoo where T: ContainsFoo {} // expected-note {{requirement from conditional conformance of 'Conditional<T>' to 'ContainsFoo'}}
44+
extension Conditional: ContainsFoo where T: ContainsFoo {}
4645

4746
extension Conditional {
4847
func f() {
@@ -53,9 +52,8 @@ extension Conditional {
5352
// expected-warning@-1{{use of 'min' as reference to global function in module 'Swift' will change in future versions of Swift to reference instance method in generic struct 'Conditional' which comes via a conditional conformance}}
5453
// expected-note@-2{{use 'Swift.' to continue to reference the global function}}
5554

56-
// FIXME(diagnostics): Same as line 39, there should be only one error here about shadowing.
5755
_ = foo(5, 6)
58-
// expected-error@-1 {{referencing instance method 'foo()' on 'Conditional' requires that 'T' conform to 'ContainsFoo'}}
59-
// expected-error@-2 {{argument passed to call that takes no arguments}}
56+
// expected-error@-1 {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}}
57+
// expected-note@-2 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}}
6058
}
6159
}

test/Sema/circular_decl_checking.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ class HasGenericFunc {
2323
}
2424
}
2525

26-
class HasProp {
26+
class HasProp { // expected-note {{'HasProp' declared here}}
2727
var HasProp: HasProp {
28-
return HasProp() // expected-error {{cannot call value of non-function type 'HasProp'}}{{19-21=}}
28+
return HasProp() // expected-error {{use of 'HasProp' refers to instance method rather than class 'HasProp' in module 'circular_decl_checking'}}
29+
// expected-note@-1 {{use 'circular_decl_checking.' to reference the class in module 'circular_decl_checking'}} {{12-12=circular_decl_checking.}}
2930
}
3031
var SomethingElse: SomethingElse? { // expected-error {{use of undeclared type 'SomethingElse'}}
3132
return nil

0 commit comments

Comments
 (0)