Skip to content

Commit c868378

Browse files
committed
ConstraintSystem: Use scoring to implement MemberImportVisibility.
Previously, the constraint solver would first attempt member lookup that excluded members from transitively imported modules. If there were no viable candidates, it would perform a second lookup that included the previously excluded members, treating any candidates as unviable. This meant that if the member reference did resolve to one of the unviable candidates the resulting AST would be broken, which could cause unwanted knock-on diagnostics. Now, members from transitively imported modules are always returned in the set of viable candidates. However, scoring will always prioritize candidates from directly imported modules over members from transitive imports. This solves the ambiguities that `MemberImportVisibility` is designed to prevent. If the only viable candidates are from transitively imported modules, though, then the reference will be resolved successfully and diagnosed later in `MiscDiagnostics.cpp`. The resulting AST will not contain any errors, which ensures that necessary access levels can be computed correctly for the imports suggested by `MemberImportVisibility` fix-its. Resolves rdar://126637855.
1 parent e5abfcb commit c868378

16 files changed

+114
-24
lines changed

include/swift/AST/DeclContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,9 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
680680
/// Looks up a precedence group with a given \p name.
681681
PrecedenceGroupLookupResult lookupPrecedenceGroup(Identifier name) const;
682682

683+
/// Returns true if the parent module of \p decl is imported by this context.
684+
bool isDeclImported(const Decl *decl) const;
685+
683686
/// Return the ASTContext for a specified DeclContext by
684687
/// walking up to the enclosing module and returning its ASTContext.
685688
LLVM_READONLY

include/swift/AST/SourceFile.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -507,11 +507,6 @@ class SourceFile final : public FileUnit {
507507
MissingImportForMemberDiagnostics[decl].push_back(loc);
508508
}
509509

510-
/// Returns true if there is a pending missing import diagnostic for \p decl.
511-
bool hasDelayedMissingImportForMemberDiagnostic(const ValueDecl *decl) const {
512-
return MissingImportForMemberDiagnostics.contains(decl);
513-
}
514-
515510
DelayedMissingImportForMemberDiags
516511
takeDelayedMissingImportForMemberDiagnostics() {
517512
DelayedMissingImportForMemberDiags diags;

include/swift/Sema/ConstraintSystem.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,8 @@ enum ScoreKind: unsigned int {
960960
SK_Hole,
961961
/// A reference to an @unavailable declaration.
962962
SK_Unavailable,
963+
/// A reference to a declaration from a module that has not been imported.
964+
SK_MissingImport,
963965
/// A reference to an async function in a synchronous context.
964966
///
965967
/// \note Any score kind after this is considered a conversion that doesn't
@@ -1124,6 +1126,9 @@ struct Score {
11241126
case SK_Unavailable:
11251127
return "use of an unavailable declaration";
11261128

1129+
case SK_MissingImport:
1130+
return "use of a declaration that has not been imported";
1131+
11271132
case SK_AsyncInSyncMismatch:
11281133
return "async-in-synchronous mismatch";
11291134

lib/AST/NameLookup.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,8 +2325,7 @@ static bool missingImportForMemberDecl(const DeclContext *dc, ValueDecl *decl) {
23252325
if (!ctx.LangOpts.hasFeature(Feature::MemberImportVisibility))
23262326
return false;
23272327

2328-
auto declModule = decl->getDeclContext()->getParentModule();
2329-
return !ctx.getImportCache().isImportedBy(declModule, dc);
2328+
return !dc->isDeclImported(decl);
23302329
}
23312330

23322331
/// Determine whether the given declaration is an acceptable lookup
@@ -2504,6 +2503,11 @@ bool DeclContext::lookupQualified(Type type,
25042503
loc, options, decls);
25052504
}
25062505

2506+
bool DeclContext::isDeclImported(const Decl *decl) const {
2507+
auto declModule = decl->getDeclContext()->getParentModule();
2508+
return getASTContext().getImportCache().isImportedBy(declModule, this);
2509+
}
2510+
25072511
static void installPropertyWrapperMembersIfNeeded(NominalTypeDecl *target,
25082512
DeclNameRef member) {
25092513
auto &Context = target->getASTContext();

lib/Sema/CSRanking.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -963,7 +963,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
963963
? SolutionCompareResult::Better
964964
: SolutionCompareResult::Worse;
965965
}
966-
966+
967967
// Compute relative score.
968968
unsigned score1 = 0;
969969
unsigned score2 = 0;

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10346,7 +10346,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
1034610346
// include them in the unviable candidates list.
1034710347
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
1034810348
includeInaccessibleMembers) {
10349-
NameLookupOptions lookupOptions = defaultMemberLookupOptions;
10349+
NameLookupOptions lookupOptions =
10350+
defaultConstraintSolverMemberLookupOptions;
1035010351

1035110352
// Local function that looks up additional candidates using the given lookup
1035210353
// options, recording the results as unviable candidates.

lib/Sema/ConstraintSystem.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,8 @@ LookupResult &ConstraintSystem::lookupMember(Type base, DeclNameRef name,
292292
if (result) return *result;
293293

294294
// Lookup the member.
295-
result = TypeChecker::lookupMember(DC, base, name, loc,
296-
defaultMemberLookupOptions);
295+
result = TypeChecker::lookupMember(
296+
DC, base, name, loc, defaultConstraintSolverMemberLookupOptions);
297297

298298
// If we are in an @_unsafeInheritExecutor context, swap out
299299
// declarations for their _unsafeInheritExecutor_ counterparts if they
@@ -3966,6 +3966,12 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
39663966
if (isDeclUnavailable(decl, locator))
39673967
increaseScore(SK_Unavailable, locator);
39683968

3969+
// If the declaration is from a module that hasn't been imported, note that.
3970+
if (getASTContext().LangOpts.hasFeature(Feature::MemberImportVisibility)) {
3971+
if (!useDC->isDeclImported(decl))
3972+
increaseScore(SK_MissingImport, locator);
3973+
}
3974+
39693975
// If this overload is disfavored, note that.
39703976
if (decl->getAttrs().hasAttribute<DisfavoredOverloadAttr>())
39713977
increaseScore(SK_DisfavoredOverload, locator);

lib/Sema/MiscDiagnostics.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6163,6 +6163,40 @@ diagnoseDictionaryLiteralDuplicateKeyEntries(const Expr *E,
61636163
const_cast<Expr *>(E)->walk(Walker);
61646164
}
61656165

6166+
// Verifies that references to members are valid under the restrictions of the
6167+
// MemberImportVisibility feature.
6168+
static void diagnoseMissingMemberImports(const Expr *E, const DeclContext *DC) {
6169+
class DiagnoseWalker : public BaseDiagnosticWalker {
6170+
const DeclContext *dc;
6171+
6172+
public:
6173+
DiagnoseWalker(const DeclContext *dc) : dc(dc) {}
6174+
6175+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
6176+
if (auto declRef = E->getReferencedDecl())
6177+
checkDecl(declRef.getDecl(), E->getLoc());
6178+
6179+
return Action::Continue(E);
6180+
}
6181+
6182+
void checkDecl(const ValueDecl *decl, SourceLoc loc) {
6183+
// Only diagnose uses of members.
6184+
if (!decl->getDeclContext()->isTypeContext())
6185+
return;
6186+
6187+
if (!dc->isDeclImported(decl))
6188+
maybeDiagnoseMissingImportForMember(decl, dc, loc);
6189+
}
6190+
};
6191+
6192+
auto &ctx = DC->getASTContext();
6193+
if (!ctx.LangOpts.hasFeature(Feature::MemberImportVisibility))
6194+
return;
6195+
6196+
DiagnoseWalker walker(DC);
6197+
const_cast<Expr *>(E)->walk(walker);
6198+
}
6199+
61666200
//===----------------------------------------------------------------------===//
61676201
// High-level entry points.
61686202
//===----------------------------------------------------------------------===//
@@ -6189,6 +6223,7 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
61896223
diagnoseConstantArgumentRequirement(E, DC);
61906224
diagUnqualifiedAccessToMethodNamedSelf(E, DC);
61916225
diagnoseDictionaryLiteralDuplicateKeyEntries(E, DC);
6226+
diagnoseMissingMemberImports(E, DC);
61926227
}
61936228

61946229
void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,7 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
279279
// Some diagnostics emitted with the `MemberImportVisibility` feature enabled
280280
// subsume these diagnostics.
281281
if (originKind == DisallowedOriginKind::MissingImport &&
282-
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility) && SF &&
283-
SF->hasDelayedMissingImportForMemberDiagnostic(D))
282+
ctx.LangOpts.hasFeature(Feature::MemberImportVisibility) && SF)
284283
return false;
285284

286285
if (auto accessor = dyn_cast<AccessorDecl>(D)) {

lib/Sema/TypeChecker.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ inline NameLookupOptions operator|(NameLookupFlags flag1,
174174
/// Default options for member name lookup.
175175
const NameLookupOptions defaultMemberLookupOptions;
176176

177+
/// Default options for member name lookup in the constraint solver.
178+
/// Overloads which come from modules that have not been imported should be
179+
/// deprioritized by ranking and diagnosed by MiscDiagnostics, so we allow
180+
/// them to be found in constraint solver lookups to improve diagnostics
181+
/// overall.
182+
const NameLookupOptions defaultConstraintSolverMemberLookupOptions(
183+
NameLookupFlags::IgnoreMissingImports);
184+
177185
/// Default options for member type lookup.
178186
const NameLookupOptions defaultMemberTypeLookupOptions;
179187

0 commit comments

Comments
 (0)