Skip to content

Commit 94e999a

Browse files
committed
Sema: Pull availability checking out of resolveType()
We used to diagnose references to unavailable declarations in two places: - inside Exprs, right after type checking the expression - inside TypeReprs, from resolveType() In broad terms, resolveType() is called with TypeReprs stored inside both Stmts and Decls. To handle the first case, I added a new overload of diagAvailability() that takes a Stmt, to be called from typeCheckStmt(). This doesn't actually walk into any Exprs stored inside the statement; this means it only walks Patterns and such. For the second case, a new DeclAvailabilityChecker is now defined in TypeCheckAccess.cpp. It's structure is analogous to the other three walkers there: - AccessControlChecker - UsableFromInlineChecker - ExportabilityChecker The new implementation of availability checking for types introduces a lot more code than the old online logic it replaces. However, I hope to consolidate some of the code duplication among the four checkers that are defined in TypeCheckAccess.cpp, and do some other cleanups that will make the benefit of the new approach apparent.
1 parent 3d0e35e commit 94e999a

30 files changed

+588
-127
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4607,7 +4607,9 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
46074607
checkActorIsolation(E, DC);
46084608
}
46094609

4610-
void swift::performStmtDiagnostics(ASTContext &ctx, const Stmt *S) {
4610+
void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {
4611+
auto &ctx = DC->getASTContext();
4612+
46114613
TypeChecker::checkUnsupportedProtocolType(ctx, const_cast<Stmt *>(S));
46124614

46134615
if (auto switchStmt = dyn_cast<SwitchStmt>(S))
@@ -4619,6 +4621,9 @@ void swift::performStmtDiagnostics(ASTContext &ctx, const Stmt *S) {
46194621
if (auto *lcs = dyn_cast<LabeledConditionalStmt>(S))
46204622
for (const auto &elt : lcs->getCond())
46214623
checkImplicitPromotionsInCondition(elt, ctx);
4624+
4625+
if (!ctx.LangOpts.DisableAvailabilityChecking)
4626+
diagAvailability(S, const_cast<DeclContext*>(DC));
46224627
}
46234628

46244629
//===----------------------------------------------------------------------===//

lib/Sema/MiscDiagnostics.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void performSyntacticExprDiagnostics(const Expr *E, const DeclContext *DC,
3838
bool isExprStmt);
3939

4040
/// Emit diagnostics for a given statement.
41-
void performStmtDiagnostics(ASTContext &ctx, const Stmt *S);
41+
void performStmtDiagnostics(const Stmt *S, DeclContext *DC);
4242

4343
void performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD);
4444

lib/Sema/TypeCheckAccess.cpp

Lines changed: 263 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "TypeCheckAccess.h"
18+
#include "TypeChecker.h"
19+
#include "TypeCheckAvailability.h"
1820
#include "TypeAccessScopeChecker.h"
1921
#include "swift/AST/ASTVisitor.h"
2022
#include "swift/AST/ASTWalker.h"
@@ -1772,7 +1774,11 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
17721774
checkOverride(VD);
17731775
}
17741776

1775-
DeclVisitor<ExportabilityChecker>::visit(D);
1777+
// Note: references to @_spi and @_implementationOnly declarations from
1778+
// @inlinable code are diagnosed by DeclAvailabilityChecker below.
1779+
auto *DC = D->getInnermostDeclContext();
1780+
if (DC->getFragileFunctionKind().kind == FragileFunctionKind::None)
1781+
DeclVisitor<ExportabilityChecker>::visit(D);
17761782
}
17771783

17781784
// Force all kinds to be handled at a lower level.
@@ -2035,6 +2041,261 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
20352041
});
20362042
}
20372043
};
2044+
2045+
/// Diagnose declarations whose signatures refer to unavailable types.
2046+
class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
2047+
DeclContext *DC;
2048+
2049+
void checkType(Type type, const TypeRepr *typeRepr, const Decl *context,
2050+
bool allowUnavailableProtocol=false) {
2051+
// Don't bother checking errors.
2052+
if (type && type->hasError())
2053+
return;
2054+
2055+
// Check the TypeRepr for references to unavailable declarations.
2056+
if (typeRepr) {
2057+
DeclAvailabilityFlags flags = None;
2058+
2059+
// We allow a type to conform to a protocol that is less available than
2060+
// the type itself. This enables a type to retroactively model or directly
2061+
// conform to a protocol only available on newer OSes and yet still be used on
2062+
// older OSes.
2063+
//
2064+
// To support this, inside inheritance clauses we allow references to
2065+
// protocols that are unavailable in the current type refinement context.
2066+
if (allowUnavailableProtocol)
2067+
flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol;
2068+
2069+
diagnoseTypeReprAvailability(typeRepr, DC, flags);
2070+
}
2071+
2072+
// Check the type for references to unavailable conformances.
2073+
if (type)
2074+
if (!context->getDeclContext()->isLocalContext())
2075+
diagnoseTypeAvailability(type, context->getLoc(), DC);
2076+
}
2077+
2078+
void checkGenericParams(const GenericContext *ownerCtx,
2079+
const ValueDecl *ownerDecl) {
2080+
// FIXME: What if we have a where clause and no generic params?
2081+
const auto params = ownerCtx->getGenericParams();
2082+
if (!params)
2083+
return;
2084+
2085+
for (auto param : *params) {
2086+
if (param->getInherited().empty())
2087+
continue;
2088+
assert(param->getInherited().size() == 1);
2089+
auto inherited = param->getInherited().front();
2090+
checkType(inherited.getType(), inherited.getTypeRepr(), ownerDecl);
2091+
}
2092+
2093+
forAllRequirementTypes(WhereClauseOwner(
2094+
const_cast<GenericContext *>(ownerCtx)),
2095+
[&](Type type, TypeRepr *typeRepr) {
2096+
checkType(type, typeRepr, ownerDecl);
2097+
});
2098+
}
2099+
2100+
public:
2101+
explicit DeclAvailabilityChecker(Decl *D)
2102+
: DC(D->getInnermostDeclContext()) {}
2103+
2104+
void visit(Decl *D) {
2105+
if (D->isImplicit())
2106+
return;
2107+
2108+
DeclVisitor<DeclAvailabilityChecker>::visit(D);
2109+
}
2110+
2111+
// Force all kinds to be handled at a lower level.
2112+
void visitDecl(Decl *D) = delete;
2113+
void visitValueDecl(ValueDecl *D) = delete;
2114+
2115+
#define UNREACHABLE(KIND, REASON) \
2116+
void visit##KIND##Decl(KIND##Decl *D) { \
2117+
llvm_unreachable(REASON); \
2118+
}
2119+
UNREACHABLE(Import, "not applicable")
2120+
UNREACHABLE(TopLevelCode, "not applicable")
2121+
UNREACHABLE(Module, "not applicable")
2122+
2123+
UNREACHABLE(Param, "handled by the enclosing declaration")
2124+
UNREACHABLE(GenericTypeParam, "handled by the enclosing declaration")
2125+
UNREACHABLE(MissingMember, "handled by the enclosing declaration")
2126+
#undef UNREACHABLE
2127+
2128+
#define UNINTERESTING(KIND) \
2129+
void visit##KIND##Decl(KIND##Decl *D) {}
2130+
2131+
UNINTERESTING(PrefixOperator) // Does not reference other decls.
2132+
UNINTERESTING(PostfixOperator) // Does not reference other decls.
2133+
UNINTERESTING(InfixOperator) // Does not reference other decls.
2134+
UNINTERESTING(IfConfig) // Not applicable.
2135+
UNINTERESTING(PoundDiagnostic) // Not applicable.
2136+
UNINTERESTING(EnumCase) // Handled at the EnumElement level.
2137+
UNINTERESTING(Destructor) // Always correct.
2138+
UNINTERESTING(Accessor) // Handled by the Var or Subscript.
2139+
UNINTERESTING(OpaqueType) // TODO
2140+
UNINTERESTING(PrecedenceGroup) // Doesn't reference anything with availability.
2141+
2142+
// Handled at the PatternBinding level; if the pattern has a simple
2143+
// "name: TheType" form, we can get better results by diagnosing the TypeRepr.
2144+
UNINTERESTING(Var)
2145+
2146+
/// \see visitPatternBindingDecl
2147+
void checkNamedPattern(const NamedPattern *NP,
2148+
const llvm::DenseSet<const VarDecl *> &seenVars) {
2149+
const VarDecl *theVar = NP->getDecl();
2150+
2151+
// Only check the type of individual variables if we didn't check an
2152+
// enclosing TypedPattern.
2153+
if (seenVars.count(theVar))
2154+
return;
2155+
2156+
checkType(theVar->getValueInterfaceType(), /*typeRepr*/nullptr, theVar);
2157+
}
2158+
2159+
/// \see visitPatternBindingDecl
2160+
void checkTypedPattern(PatternBindingDecl *PBD,
2161+
const TypedPattern *TP,
2162+
llvm::DenseSet<const VarDecl *> &seenVars) {
2163+
// FIXME: We need to figure out if this is a stored or computed property,
2164+
// so we pull out some random VarDecl in the pattern. They're all going to
2165+
// be the same, but still, ick.
2166+
const VarDecl *anyVar = nullptr;
2167+
TP->forEachVariable([&](VarDecl *V) {
2168+
seenVars.insert(V);
2169+
anyVar = V;
2170+
});
2171+
2172+
checkType(TP->hasType() ? TP->getType() : Type(),
2173+
TP->getTypeRepr(), PBD);
2174+
2175+
// Check the property wrapper types.
2176+
if (anyVar)
2177+
for (auto attr : anyVar->getAttachedPropertyWrappers())
2178+
checkType(attr->getType(), attr->getTypeRepr(), anyVar);
2179+
}
2180+
2181+
void visitPatternBindingDecl(PatternBindingDecl *PBD) {
2182+
llvm::DenseSet<const VarDecl *> seenVars;
2183+
for (auto idx : range(PBD->getNumPatternEntries())) {
2184+
PBD->getPattern(idx)->forEachNode([&](const Pattern *P) {
2185+
if (auto *NP = dyn_cast<NamedPattern>(P)) {
2186+
checkNamedPattern(NP, seenVars);
2187+
return;
2188+
}
2189+
2190+
auto *TP = dyn_cast<TypedPattern>(P);
2191+
if (!TP)
2192+
return;
2193+
checkTypedPattern(PBD, TP, seenVars);
2194+
});
2195+
seenVars.clear();
2196+
}
2197+
}
2198+
2199+
void visitTypeAliasDecl(TypeAliasDecl *TAD) {
2200+
checkGenericParams(TAD, TAD);
2201+
checkType(TAD->getUnderlyingType(),
2202+
TAD->getUnderlyingTypeRepr(), TAD);
2203+
}
2204+
2205+
void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) {
2206+
llvm::for_each(assocType->getInherited(),
2207+
[&](TypeLoc requirement) {
2208+
checkType(requirement.getType(), requirement.getTypeRepr(),
2209+
assocType);
2210+
});
2211+
checkType(assocType->getDefaultDefinitionType(),
2212+
assocType->getDefaultDefinitionTypeRepr(), assocType);
2213+
2214+
if (assocType->getTrailingWhereClause()) {
2215+
forAllRequirementTypes(assocType,
2216+
[&](Type type, TypeRepr *typeRepr) {
2217+
checkType(type, typeRepr, assocType);
2218+
});
2219+
}
2220+
}
2221+
2222+
void visitNominalTypeDecl(const NominalTypeDecl *nominal) {
2223+
checkGenericParams(nominal, nominal);
2224+
2225+
llvm::for_each(nominal->getInherited(),
2226+
[&](TypeLoc inherited) {
2227+
checkType(inherited.getType(), inherited.getTypeRepr(),
2228+
nominal, /*allowUnavailableProtocol=*/true);
2229+
});
2230+
}
2231+
2232+
void visitProtocolDecl(ProtocolDecl *proto) {
2233+
llvm::for_each(proto->getInherited(),
2234+
[&](TypeLoc requirement) {
2235+
checkType(requirement.getType(), requirement.getTypeRepr(), proto,
2236+
/*allowUnavailableProtocol=*/true);
2237+
});
2238+
2239+
if (proto->getTrailingWhereClause()) {
2240+
forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) {
2241+
checkType(type, typeRepr, proto);
2242+
});
2243+
}
2244+
}
2245+
2246+
void visitSubscriptDecl(SubscriptDecl *SD) {
2247+
checkGenericParams(SD, SD);
2248+
2249+
for (auto &P : *SD->getIndices()) {
2250+
checkType(P->getInterfaceType(), P->getTypeRepr(), SD);
2251+
}
2252+
checkType(SD->getElementInterfaceType(), SD->getElementTypeRepr(), SD);
2253+
}
2254+
2255+
void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) {
2256+
checkGenericParams(fn, fn);
2257+
2258+
for (auto *P : *fn->getParameters())
2259+
checkType(P->getInterfaceType(), P->getTypeRepr(), fn);
2260+
}
2261+
2262+
void visitFuncDecl(FuncDecl *FD) {
2263+
visitAbstractFunctionDecl(FD);
2264+
checkType(FD->getResultInterfaceType(), FD->getResultTypeRepr(), FD);
2265+
}
2266+
2267+
void visitEnumElementDecl(EnumElementDecl *EED) {
2268+
if (!EED->hasAssociatedValues())
2269+
return;
2270+
for (auto &P : *EED->getParameterList())
2271+
checkType(P->getInterfaceType(), P->getTypeRepr(), EED);
2272+
}
2273+
2274+
void checkConstrainedExtensionRequirements(ExtensionDecl *ED) {
2275+
if (!ED->getTrailingWhereClause())
2276+
return;
2277+
forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) {
2278+
checkType(type, typeRepr, ED);
2279+
});
2280+
}
2281+
2282+
void visitExtensionDecl(ExtensionDecl *ED) {
2283+
auto extendedType = ED->getExtendedNominal();
2284+
assert(extendedType && "valid extension with no extended type?");
2285+
if (!extendedType)
2286+
return;
2287+
2288+
llvm::for_each(ED->getInherited(),
2289+
[&](TypeLoc inherited) {
2290+
checkType(inherited.getType(), inherited.getTypeRepr(),
2291+
ED, /*allowUnavailableProtocol=*/true);
2292+
});
2293+
2294+
checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED);
2295+
checkConstrainedExtensionRequirements(ED);
2296+
}
2297+
};
2298+
20382299
} // end anonymous namespace
20392300

20402301
static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) {
@@ -2086,4 +2347,5 @@ void swift::checkAccessControl(Decl *D) {
20862347
}
20872348

20882349
ExportabilityChecker().visit(D);
2350+
DeclAvailabilityChecker(D).visit(D);
20892351
}

0 commit comments

Comments
 (0)