mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Sema: Fix a crash in migrate mode for MemberImportVisibility.
`TypeChecker::resolveDeclRefExpr()` would leave error nodes in the AST when performing fallback name lookups with `MemberImportVisibility`. When running in migration mode for `MemberImportVisibility`, these error nodes would then cause the compiler to either diagnose a missing error or to crash during SILGen. Instead of restricting name lookup during `TypeChecker::resolveDeclRefExpr()`, allow it to find candidates with missing imports and then diagnose them if necessary before forming a resolve decl ref. Resolves rdar://154361861.
This commit is contained in:
@@ -568,6 +568,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
|
|||||||
// chance to diagnose name shadowing which requires explicit
|
// chance to diagnose name shadowing which requires explicit
|
||||||
// name/module qualifier to access top-level name.
|
// name/module qualifier to access top-level name.
|
||||||
lookupOptions |= NameLookupFlags::IncludeOuterResults;
|
lookupOptions |= NameLookupFlags::IncludeOuterResults;
|
||||||
|
lookupOptions |= NameLookupFlags::IgnoreMissingImports;
|
||||||
|
|
||||||
LookupResult Lookup;
|
LookupResult Lookup;
|
||||||
|
|
||||||
@@ -662,19 +663,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
|
|||||||
return errorResult();
|
return errorResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try ignoring missing imports.
|
|
||||||
relookupOptions |= NameLookupFlags::IgnoreMissingImports;
|
|
||||||
auto nonImportedResults =
|
|
||||||
TypeChecker::lookupUnqualified(DC, LookupName, Loc, relookupOptions);
|
|
||||||
if (nonImportedResults) {
|
|
||||||
const ValueDecl *first = nonImportedResults.front().getValueDecl();
|
|
||||||
maybeDiagnoseMissingImportForMember(first, DC, Loc);
|
|
||||||
|
|
||||||
// Don't try to recover here; we'll get more access-related diagnostics
|
|
||||||
// downstream if the type of the inaccessible decl is also inaccessible.
|
|
||||||
return errorResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Name will be a compound name if it was written explicitly as
|
// TODO: Name will be a compound name if it was written explicitly as
|
||||||
// one, but we should also try to propagate labels into this.
|
// one, but we should also try to propagate labels into this.
|
||||||
DeclNameLoc nameLoc = UDRE->getNameLoc();
|
DeclNameLoc nameLoc = UDRE->getNameLoc();
|
||||||
@@ -791,6 +779,14 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
|
|||||||
// FIXME: Need to refactor the way we build an AST node from a lookup result!
|
// FIXME: Need to refactor the way we build an AST node from a lookup result!
|
||||||
|
|
||||||
auto buildTypeExpr = [&](TypeDecl *D) -> Expr * {
|
auto buildTypeExpr = [&](TypeDecl *D) -> Expr * {
|
||||||
|
auto *LookupDC = Lookup[0].getDeclContext();
|
||||||
|
|
||||||
|
// If the type decl is a member that wasn't imported, diagnose it now when
|
||||||
|
// MemberImportVisibility is enabled.
|
||||||
|
if (!UDRE->isImplicit() && LookupDC)
|
||||||
|
maybeDiagnoseMissingImportForMember(D, LookupDC,
|
||||||
|
UDRE->getNameLoc().getStartLoc());
|
||||||
|
|
||||||
// FIXME: This is odd.
|
// FIXME: This is odd.
|
||||||
if (isa<ModuleDecl>(D)) {
|
if (isa<ModuleDecl>(D)) {
|
||||||
return new (Context) DeclRefExpr(
|
return new (Context) DeclRefExpr(
|
||||||
@@ -798,7 +794,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
|
|||||||
/*Implicit=*/false, AccessSemantics::Ordinary, D->getInterfaceType());
|
/*Implicit=*/false, AccessSemantics::Ordinary, D->getInterfaceType());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *LookupDC = Lookup[0].getDeclContext();
|
|
||||||
bool makeTypeValue = false;
|
bool makeTypeValue = false;
|
||||||
|
|
||||||
if (isa<GenericTypeParamDecl>(D) &&
|
if (isa<GenericTypeParamDecl>(D) &&
|
||||||
@@ -890,6 +885,13 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
|
|||||||
DC, UDRE->getNameLoc().getBaseNameLoc(), ResultValues);
|
DC, UDRE->getNameLoc().getBaseNameLoc(), ResultValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is a single result and it's a member that wasn't imported,
|
||||||
|
// diagnose it now when MemberImportVisibility is enabled.
|
||||||
|
if (ResultValues.size() == 1) {
|
||||||
|
maybeDiagnoseMissingImportForMember(ResultValues.front(), DC,
|
||||||
|
UDRE->getNameLoc().getStartLoc());
|
||||||
|
}
|
||||||
|
|
||||||
return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(),
|
return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(),
|
||||||
UDRE->isImplicit(), UDRE->getFunctionRefInfo());
|
UDRE->isImplicit(), UDRE->getFunctionRefInfo());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1013,11 +1013,26 @@ bool swift::maybeDiagnoseMissingImportForMember(const ValueDecl *decl,
|
|||||||
const DeclContext *dc,
|
const DeclContext *dc,
|
||||||
SourceLoc loc,
|
SourceLoc loc,
|
||||||
DiagnosticBehavior limit) {
|
DiagnosticBehavior limit) {
|
||||||
|
// Only diagnose references in source files.
|
||||||
|
auto sf = dc->getParentSourceFile();
|
||||||
|
if (!sf)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto &ctx = dc->getASTContext();
|
||||||
|
if (!ctx.LangOpts.hasFeature(Feature::MemberImportVisibility,
|
||||||
|
/*allowMigration=*/true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Only diagnose members.
|
||||||
|
if (!decl->getDeclContext()->isTypeContext())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Only diagnose declarations that haven't been imported.
|
||||||
if (dc->isDeclImported(decl))
|
if (dc->isDeclImported(decl))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto definingModule = decl->getModuleContextForNameLookup();
|
auto definingModule = decl->getModuleContextForNameLookup();
|
||||||
if (dc->getASTContext().LangOpts.EnableCXXInterop) {
|
if (ctx.LangOpts.EnableCXXInterop) {
|
||||||
// With Cxx interop enabled, there are some declarations that always belong
|
// With Cxx interop enabled, there are some declarations that always belong
|
||||||
// to the Clang header import module which should always be implicitly
|
// to the Clang header import module which should always be implicitly
|
||||||
// visible. However, that module is not implicitly imported in source files
|
// visible. However, that module is not implicitly imported in source files
|
||||||
@@ -1026,12 +1041,6 @@ bool swift::maybeDiagnoseMissingImportForMember(const ValueDecl *decl,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sf = dc->getParentSourceFile();
|
|
||||||
if (!sf)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto &ctx = dc->getASTContext();
|
|
||||||
|
|
||||||
// In lazy typechecking mode just emit the diagnostic immediately without a
|
// In lazy typechecking mode just emit the diagnostic immediately without a
|
||||||
// fix-it since there won't be an opportunity to emit delayed diagnostics.
|
// fix-it since there won't be an opportunity to emit delayed diagnostics.
|
||||||
if (ctx.TypeCheckerOpts.EnableLazyTypecheck) {
|
if (ctx.TypeCheckerOpts.EnableLazyTypecheck) {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// RUN: %target-swift-frontend -emit-module -o %t %t/InternalUsesOnlyDefaultedImportSPIOnly.swift -I %t
|
// RUN: %target-swift-frontend -emit-module -o %t %t/InternalUsesOnlyDefaultedImportSPIOnly.swift -I %t
|
||||||
// RUN: %target-swift-frontend -emit-module -o %t %t/PublicUsesOnlySPIOnly.swift -I %t
|
// RUN: %target-swift-frontend -emit-module -o %t %t/PublicUsesOnlySPIOnly.swift -I %t
|
||||||
|
|
||||||
// RUN: %target-swift-frontend -typecheck -verify -swift-version 5 \
|
// RUN: %target-swift-frontend -emit-silgen -verify -swift-version 5 \
|
||||||
// RUN: -primary-file %t/main.swift \
|
// RUN: -primary-file %t/main.swift \
|
||||||
// RUN: %t/imports.swift \
|
// RUN: %t/imports.swift \
|
||||||
// RUN: -I %t -package-name Package \
|
// RUN: -I %t -package-name Package \
|
||||||
@@ -81,6 +81,31 @@ extension Int {
|
|||||||
internal func usesTypealiasInMixedUses_Internal(x: TypealiasInMixedUses) {} // expected-note {{type alias 'TypealiasInMixedUses' from 'MixedUses' used here}}
|
internal func usesTypealiasInMixedUses_Internal(x: TypealiasInMixedUses) {} // expected-note {{type alias 'TypealiasInMixedUses' from 'MixedUses' used here}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GenericType<T> { }
|
||||||
|
|
||||||
|
extension Int {
|
||||||
|
var referencesMemberInInternalUsesOnly: Int { memberInInternalUsesOnly } // expected-note {{property 'memberInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
|
||||||
|
func testTypes<T: ProtocolInInternalUsesOnly>(_ t: T) -> TypealiasInInternalUsesOnly? {
|
||||||
|
// expected-note@-1 {{protocol 'ProtocolInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
// expected-note@-2 {{type alias 'TypealiasInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
|
||||||
|
let _: TypealiasInInternalUsesOnly = 0 // expected-note {{type alias 'TypealiasInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
_ = TypealiasInInternalUsesOnly.self // expected-note {{type alias 'TypealiasInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
_ = (TypealiasInInternalUsesOnly).self // expected-note {{type alias 'TypealiasInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
_ = (Int, TypealiasInInternalUsesOnly).self // expected-note {{type alias 'TypealiasInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
_ = GenericType<TypealiasInInternalUsesOnly>.self // expected-note {{type alias 'TypealiasInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
return t as? TypealiasInInternalUsesOnly // expected-note {{type alias 'TypealiasInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLeadingDotSyntax() {
|
||||||
|
func takesP<T: ProtocolInInternalUsesOnly>(_: T) { } // expected-note {{protocol 'ProtocolInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
takesP(.staticDefaultMemberInInternalUsesOnly) // expected-note {{static property 'staticDefaultMemberInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Int.TypealiasInInternalUsesOnly { } // expected-note {{type alias 'TypealiasInInternalUsesOnly' from 'InternalUsesOnly' used here}}
|
||||||
|
|
||||||
//--- imports.swift
|
//--- imports.swift
|
||||||
|
|
||||||
internal import InternalUsesOnly
|
internal import InternalUsesOnly
|
||||||
@@ -97,10 +122,17 @@ internal import ImportsOtherModules
|
|||||||
//--- InternalUsesOnly.swift
|
//--- InternalUsesOnly.swift
|
||||||
|
|
||||||
extension Int {
|
extension Int {
|
||||||
|
public protocol ProtocolInInternalUsesOnly { }
|
||||||
public typealias TypealiasInInternalUsesOnly = Self
|
public typealias TypealiasInInternalUsesOnly = Self
|
||||||
public var memberInInternalUsesOnly: Int { return self }
|
public var memberInInternalUsesOnly: Int { return self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Int: Int.ProtocolInInternalUsesOnly { }
|
||||||
|
|
||||||
|
extension Int.ProtocolInInternalUsesOnly where Self == Int {
|
||||||
|
public static var staticDefaultMemberInInternalUsesOnly: Int { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
//--- InternalUsesOnlyDefaultedImport.swift
|
//--- InternalUsesOnlyDefaultedImport.swift
|
||||||
|
|
||||||
extension Int {
|
extension Int {
|
||||||
|
|||||||
Reference in New Issue
Block a user