Skip to content

Implementation-only import checking for types used in decls #23722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 53 additions & 21 deletions include/swift/AST/AccessScopeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,47 +23,79 @@

namespace swift {

class AbstractStorageDecl;
class ExtensionDecl;
class BoundGenericType;
class ComponentIdentTypeRepr;
class NominalType;
class SourceFile;
class ValueDecl;
class TypeAliasType;

class AccessScopeChecker {
const SourceFile *File;
bool TreatUsableFromInlineAsPublic;

protected:
ASTContext &Context;
Optional<AccessScope> Scope = AccessScope::getPublic();

AccessScopeChecker(const DeclContext *useDC,
bool treatUsableFromInlineAsPublic);
bool visitDecl(ValueDecl *VD);
};

class TypeReprAccessScopeChecker : private ASTWalker, AccessScopeChecker {
TypeReprAccessScopeChecker(const DeclContext *useDC,
bool treatUsableFromInlineAsPublic);

bool walkToTypeReprPre(TypeRepr *TR) override;
bool walkToTypeReprPost(TypeRepr *TR) override;
bool visitDecl(const ValueDecl *VD);

public:
static Optional<AccessScope>
getAccessScope(TypeRepr *TR, const DeclContext *useDC,
bool treatUsableFromInlineAsPublic = false);
static Optional<AccessScope>
getAccessScope(Type T, const DeclContext *useDC,
bool treatUsableFromInlineAsPublic = false);
};

class TypeAccessScopeChecker : private TypeWalker, AccessScopeChecker {
TypeAccessScopeChecker(const DeclContext *useDC,
bool treatUsableFromInlineAsPublic);
/// Walks a Type to find all NominalTypes, BoundGenericTypes, and
/// TypeAliasTypes.
class TypeDeclFinder : public TypeWalker {
Action walkToTypePre(Type T) override;

public:
virtual Action visitNominalType(const NominalType *ty) {
return Action::Continue;
}
virtual Action visitBoundGenericType(const BoundGenericType *ty) {
return Action::Continue;
}
virtual Action visitTypeAliasType(const TypeAliasType *ty) {
return Action::Continue;
}
};

Action walkToTypePre(Type T);
/// A TypeDeclFinder for use cases where all types should be treated
/// equivalently and where generic arguments can be walked to separately from
/// the generic type.
class SimpleTypeDeclFinder : public TypeDeclFinder {
/// The function to call when a ComponentIdentTypeRepr is seen.
llvm::function_ref<Action(const TypeDecl *)> Callback;

Action visitNominalType(const NominalType *ty) override;
Action visitBoundGenericType(const BoundGenericType *ty) override;
Action visitTypeAliasType(const TypeAliasType *ty) override;

public:
static Optional<AccessScope>
getAccessScope(Type T, const DeclContext *useDC,
bool treatUsableFromInlineAsPublic = false);
explicit SimpleTypeDeclFinder(
llvm::function_ref<Action(const TypeDecl *)> callback)
: Callback(callback) {}
};

/// Walks a TypeRepr to find all ComponentIdentTypeReprs with bound TypeDecls.
///
/// Subclasses can either override #visitTypeDecl if they only care about
/// types on their own, or #visitComponentIdentTypeRepr if they want to keep
/// the TypeRepr around.
class TypeReprIdentFinder : public ASTWalker {
/// The function to call when a ComponentIdentTypeRepr is seen.
llvm::function_ref<bool(const ComponentIdentTypeRepr *)> Callback;

bool walkToTypeReprPost(TypeRepr *TR) override;
public:
explicit TypeReprIdentFinder(
llvm::function_ref<bool(const ComponentIdentTypeRepr *)> callback)
: Callback(callback) {}
};

}
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2392,6 +2392,11 @@ NOTE(enum_raw_value_incrementing_from_zero,none,
NOTE(construct_raw_representable_from_unwrapped_value,none,
"construct %0 from unwrapped %1 value", (Type, Type))

ERROR(decl_from_implementation_only_module,none,
"cannot use %0 here; %1 has been imported as "
"'@_implementationOnly'",
(DeclName, Identifier))

// Derived conformances
ERROR(cannot_synthesize_init_in_extension_of_nonfinal,none,
"implementation of %0 for non-final class cannot be automatically "
Expand Down
90 changes: 49 additions & 41 deletions lib/AST/AccessScopeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,68 +24,76 @@ using namespace swift;
AccessScopeChecker::AccessScopeChecker(const DeclContext *useDC,
bool treatUsableFromInlineAsPublic)
: File(useDC->getParentSourceFile()),
TreatUsableFromInlineAsPublic(treatUsableFromInlineAsPublic),
Context(File->getASTContext()) {}
TreatUsableFromInlineAsPublic(treatUsableFromInlineAsPublic) {}

bool
AccessScopeChecker::visitDecl(ValueDecl *VD) {
if (!VD || isa<GenericTypeParamDecl>(VD))
AccessScopeChecker::visitDecl(const ValueDecl *VD) {
if (isa<GenericTypeParamDecl>(VD))
return true;

auto AS = VD->getFormalAccessScope(File, TreatUsableFromInlineAsPublic);
Scope = Scope->intersectWith(AS);
return Scope.hasValue();
}

TypeReprAccessScopeChecker::TypeReprAccessScopeChecker(const DeclContext *useDC,
bool treatUsableFromInlineAsPublic)
: AccessScopeChecker(useDC, treatUsableFromInlineAsPublic) {
bool TypeReprIdentFinder::walkToTypeReprPost(TypeRepr *TR) {
auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR);
if (!CITR || !CITR->getBoundDecl())
return true;
return Callback(CITR);
}

bool
TypeReprAccessScopeChecker::walkToTypeReprPre(TypeRepr *TR) {
if (auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR))
return visitDecl(CITR->getBoundDecl());
return true;
Optional<AccessScope>
AccessScopeChecker::getAccessScope(TypeRepr *TR, const DeclContext *useDC,
bool treatUsableFromInlineAsPublic) {
AccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
TR->walk(TypeReprIdentFinder([&](const ComponentIdentTypeRepr *typeRepr) {
return checker.visitDecl(typeRepr->getBoundDecl());
}));
return checker.Scope;
}

bool
TypeReprAccessScopeChecker::walkToTypeReprPost(TypeRepr *TR) {
return Scope.hasValue();
}
TypeWalker::Action TypeDeclFinder::walkToTypePre(Type T) {
if (auto *TAT = dyn_cast<TypeAliasType>(T.getPointer()))
return visitTypeAliasType(TAT);

Optional<AccessScope>
TypeReprAccessScopeChecker::getAccessScope(TypeRepr *TR, const DeclContext *useDC,
bool treatUsableFromInlineAsPublic) {
TypeReprAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
TR->walk(checker);
return checker.Scope;
// FIXME: We're looking through sugar here so that we visit, e.g.,
// Swift.Array when we see `[Int]`. But that means we do redundant work when
// we see sugar that's purely structural, like `(Int)`. Fortunately, paren
// types are the only such purely structural sugar at the time this comment
// was written, and they're not so common in the first place.
if (auto *BGT = T->getAs<BoundGenericType>())
return visitBoundGenericType(BGT);
if (auto *NT = T->getAs<NominalType>())
return visitNominalType(NT);

return Action::Continue;
}

TypeAccessScopeChecker::TypeAccessScopeChecker(const DeclContext *useDC,
bool treatUsableFromInlineAsPublic)
: AccessScopeChecker(useDC, treatUsableFromInlineAsPublic) {}
TypeWalker::Action
SimpleTypeDeclFinder::visitNominalType(const NominalType *ty) {
return Callback(ty->getDecl());
}

TypeWalker::Action
TypeAccessScopeChecker::walkToTypePre(Type T) {
ValueDecl *VD;
if (auto *BNAD = dyn_cast<TypeAliasType>(T.getPointer()))
VD = BNAD->getDecl();
else if (auto *NTD = T->getAnyNominal())
VD = NTD;
else
VD = nullptr;

if (!visitDecl(VD))
return Action::Stop;
SimpleTypeDeclFinder::visitBoundGenericType(const BoundGenericType *ty) {
return Callback(ty->getDecl());
}

return Action::Continue;
TypeWalker::Action
SimpleTypeDeclFinder::visitTypeAliasType(const TypeAliasType *ty) {
return Callback(ty->getDecl());
}


Optional<AccessScope>
TypeAccessScopeChecker::getAccessScope(Type T, const DeclContext *useDC,
bool treatUsableFromInlineAsPublic) {
TypeAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
T.walk(checker);
AccessScopeChecker::getAccessScope(Type T, const DeclContext *useDC,
bool treatUsableFromInlineAsPublic) {
AccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
T.walk(SimpleTypeDeclFinder([&](const ValueDecl *VD) {
if (checker.visitDecl(VD))
return TypeWalker::Action::Continue;
return TypeWalker::Action::Stop;
}));
return checker.Scope;
}
Loading