Skip to content

Commit

Permalink
Sema: Support where clauses on contextually generic decls
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyLatsis committed Mar 5, 2020
1 parent eb539e6 commit 6f08216
Show file tree
Hide file tree
Showing 12 changed files with 259 additions and 101 deletions.
4 changes: 3 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1218,7 +1218,9 @@ class RequirementRepr {
void print(raw_ostream &OS) const;
void print(ASTPrinter &Printer) const;
};


using GenericParamSource = PointerUnion<GenericContext *, GenericParamList *>;

/// GenericParamList - A list of generic parameters that is part of a generic
/// function or type, along with extra requirements placed on those generic
/// parameters and types derived from them.
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2751,6 +2751,10 @@ ERROR(dynamic_self_stored_property_init,none,
ERROR(dynamic_self_default_arg,none,
"covariant 'Self' type cannot be referenced from a default argument expression", ())

ERROR(where_nongeneric_ctx,none,
"'where' clause on non-generic member declaration requires a "
"generic context", ())

//------------------------------------------------------------------------------
// MARK: Type Check Attributes
//------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -1108,7 +1108,7 @@ class InferredGenericSignatureRequest :
public SimpleRequest<InferredGenericSignatureRequest,
GenericSignature (ModuleDecl *,
GenericSignatureImpl *,
GenericParamList *,
GenericParamSource,
SmallVector<Requirement, 2>,
SmallVector<TypeLoc, 2>,
bool),
Expand All @@ -1124,7 +1124,7 @@ class InferredGenericSignatureRequest :
evaluate(Evaluator &evaluator,
ModuleDecl *module,
GenericSignatureImpl *baseSignature,
GenericParamList *gpl,
GenericParamSource paramSource,
SmallVector<Requirement, 2> addedRequirements,
SmallVector<TypeLoc, 2> inferenceSources,
bool allowConcreteGenericParams) const;
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ SWIFT_REQUEST(TypeChecker, HasDynamicMemberLookupAttributeRequest,
bool(CanType), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, InferredGenericSignatureRequest,
GenericSignature (ModuleDecl *, GenericSignatureImpl *,
GenericParamList *,
GenericParamSource,
SmallVector<Requirement, 2>,
SmallVector<TypeLoc, 2>, bool),
Cached, NoLocationInfo)
Expand Down
9 changes: 9 additions & 0 deletions include/swift/Basic/SimpleDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ namespace swift {
}
out << "}";
}

template<typename T, typename U>
void simple_display(llvm::raw_ostream &out,
const llvm::PointerUnion<T, U> &ptrUnion) {
if (const auto t = ptrUnion.template dyn_cast<T>())
simple_display(out, t);
else
simple_display(out, ptrUnion.template get<U>());
}
}

#endif // SWIFT_BASIC_SIMPLE_DISPLAY_H
4 changes: 2 additions & 2 deletions lib/AST/ASTScopeLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ bool ASTScopeImpl::doesContextMatchStartingContext(
// For a SubscriptDecl with generic parameters, the call tries to do lookups
// with startingContext equal to either the get or set subscript
// AbstractFunctionDecls. Since the generic parameters are in the
// SubScriptDeclScope, and not the AbstractFunctionDecl scopes (after all how
// could one parameter be in two scopes?), GenericParamScoped intercepts the
// SubscriptDeclScope, and not the AbstractFunctionDecl scopes (after all how
// could one parameter be in two scopes?), GenericParamScope intercepts the
// match query here and tests against the accessor DeclContexts.
bool GenericParamScope::doesContextMatchStartingContext(
const DeclContext *context) const {
Expand Down
9 changes: 6 additions & 3 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1077,9 +1077,12 @@ void GenericContext::setGenericSignature(GenericSignature genericSig) {
}

SourceRange GenericContext::getGenericTrailingWhereClauseSourceRange() const {
if (!isGeneric())
return SourceRange();
return getGenericParams()->getTrailingWhereClauseSourceRange();
if (isGeneric())
return getGenericParams()->getTrailingWhereClauseSourceRange();
else if (const auto *where = getTrailingWhereClause())
return where->getSourceRange();

return SourceRange();
}

ImportDecl *ImportDecl::create(ASTContext &Ctx, DeclContext *DC,
Expand Down
147 changes: 84 additions & 63 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7459,7 +7459,7 @@ llvm::Expected<GenericSignature>
InferredGenericSignatureRequest::evaluate(
Evaluator &evaluator, ModuleDecl *parentModule,
GenericSignatureImpl *parentSig,
GenericParamList *gpl,
GenericParamSource paramSource,
SmallVector<Requirement, 2> addedRequirements,
SmallVector<TypeLoc, 2> inferenceSources,
bool allowConcreteGenericParams) const {
Expand All @@ -7470,78 +7470,99 @@ InferredGenericSignatureRequest::evaluate(
// from that context.
builder.addGenericSignature(parentSig);

// Type check the generic parameters, treating all generic type
// parameters as dependent, unresolved.
SmallVector<GenericParamList *, 2> gpLists;
if (gpl->getOuterParameters() && !parentSig) {
for (auto *outerParams = gpl;
DeclContext *lookupDC = nullptr;

const auto visitRequirement = [&](const Requirement &req,
RequirementRepr *reqRepr) {
const auto source = FloatingRequirementSource::forExplicit(reqRepr);

// If we're extending a protocol and adding a redundant requirement,
// for example, `extension Foo where Self: Foo`, then emit a
// diagnostic.

if (auto decl = lookupDC->getAsDecl()) {
if (auto extDecl = dyn_cast<ExtensionDecl>(decl)) {
auto extType = extDecl->getDeclaredInterfaceType();
auto extSelfType = extDecl->getSelfInterfaceType();
auto reqLHSType = req.getFirstType();
auto reqRHSType = req.getSecondType();

if (extType->isExistentialType() &&
reqLHSType->isEqual(extSelfType) &&
reqRHSType->isEqual(extType)) {

auto &ctx = extDecl->getASTContext();
ctx.Diags.diagnose(extDecl->getLoc(),
diag::protocol_extension_redundant_requirement,
extType->getString(),
extSelfType->getString(),
reqRHSType->getString());
}
}
}

builder.addRequirement(req, reqRepr, source, nullptr,
lookupDC->getParentModule());
return false;
};

GenericParamList *genericParams = nullptr;
if (auto params = paramSource.dyn_cast<GenericParamList *>())
genericParams = params;
else
genericParams = paramSource.get<GenericContext *>()->getGenericParams();

if (genericParams) {
// Extensions never have a parent signature.
if (genericParams->getOuterParameters())
assert(parentSig == nullptr);

// Type check the generic parameters, treating all generic type
// parameters as dependent, unresolved.
SmallVector<GenericParamList *, 2> gpLists;
for (auto *outerParams = genericParams;
outerParams != nullptr;
outerParams = outerParams->getOuterParameters()) {
gpLists.push_back(outerParams);
}
} else {
gpLists.push_back(gpl);
}

// The generic parameter lists MUST appear from innermost to outermost.
// We walk them backwards to order outer requirements before
// inner requirements.
for (auto &genericParams : llvm::reverse(gpLists)) {
assert(genericParams->size() > 0 &&
"Parsed an empty generic parameter list?");
// The generic parameter lists MUST appear from innermost to outermost.
// We walk them backwards to order outer requirements before
// inner requirements.
for (auto &genericParams : llvm::reverse(gpLists)) {
assert(genericParams->size() > 0 &&
"Parsed an empty generic parameter list?");

// Determine where and how to perform name lookup.
DeclContext *lookupDC = genericParams->begin()[0]->getDeclContext();
// First, add the generic parameters to the generic signature builder.
// Do this before checking the inheritance clause, since it may
// itself be dependent on one of these parameters.
for (const auto param : *genericParams)
builder.addGenericParameter(param);

// First, add the generic parameters to the generic signature builder.
// Do this before checking the inheritance clause, since it may
// itself be dependent on one of these parameters.
for (auto param : *genericParams)
builder.addGenericParameter(param);
// Add the requirements for each of the generic parameters to the builder.
// Now, check the inheritance clauses of each parameter.
for (const auto param : *genericParams)
builder.addGenericParameterRequirements(param);

// Add the requirements for each of the generic parameters to the builder.
// Now, check the inheritance clauses of each parameter.
for (auto param : *genericParams)
builder.addGenericParameterRequirements(param);
// Determine where and how to perform name lookup.
lookupDC = genericParams->begin()[0]->getDeclContext();

// Add the requirements clause to the builder.
// Add the requirements clause to the builder.
WhereClauseOwner(lookupDC, genericParams)
.visitRequirements(TypeResolutionStage::Structural,
visitRequirement);
}
} else {
// The declaration has a where clause, but no generic parameters of its own.
const auto ctx = paramSource.get<GenericContext *>();

using FloatingRequirementSource =
GenericSignatureBuilder::FloatingRequirementSource;
WhereClauseOwner(lookupDC, genericParams).visitRequirements(
TypeResolutionStage::Structural,
[&](const Requirement &req, RequirementRepr *reqRepr) {
auto source = FloatingRequirementSource::forExplicit(reqRepr);

// If we're extending a protocol and adding a redundant requirement,
// for example, `extension Foo where Self: Foo`, then emit a
// diagnostic.

if (auto decl = lookupDC->getAsDecl()) {
if (auto extDecl = dyn_cast<ExtensionDecl>(decl)) {
auto extType = extDecl->getDeclaredInterfaceType();
auto extSelfType = extDecl->getSelfInterfaceType();
auto reqLHSType = req.getFirstType();
auto reqRHSType = req.getSecondType();

if (extType->isExistentialType() &&
reqLHSType->isEqual(extSelfType) &&
reqRHSType->isEqual(extType)) {

auto &ctx = extDecl->getASTContext();
ctx.Diags.diagnose(extDecl->getLoc(),
diag::protocol_extension_redundant_requirement,
extType->getString(),
extSelfType->getString(),
reqRHSType->getString());
}
}
}

builder.addRequirement(req, reqRepr, source, nullptr,
lookupDC->getParentModule());
return false;
});
assert(ctx->getTrailingWhereClause() && "No params or where clause");

// Determine where and how to perform name lookup.
lookupDC = ctx;

WhereClauseOwner(ctx).visitRequirements(
TypeResolutionStage::Structural, visitRequirement);
}

/// Perform any remaining requirement inference.
Expand Down
5 changes: 3 additions & 2 deletions lib/Parse/ParseGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,9 @@ ParserStatus Parser::parseGenericWhereClause(
}


/// Parse a free-standing where clause attached to a declaration, adding it to
/// a generic parameter list that may (or may not) already exist.
/// Parse a free-standing where clause attached to a declaration,
/// adding it to a generic parameter list, if any, or to the given
/// generic context representing the declaration.
ParserStatus Parser::
parseFreestandingGenericWhereClause(GenericContext *genCtx,
GenericParamList *&genericParams,
Expand Down
68 changes: 43 additions & 25 deletions lib/Sema/TypeCheckGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -450,16 +450,17 @@ void TypeChecker::checkReferencedGenericParams(GenericContext *dc) {
///

GenericSignature TypeChecker::checkGenericSignature(
GenericParamList *genericParamList,
GenericParamSource paramSource,
DeclContext *dc,
GenericSignature parentSig,
bool allowConcreteGenericParams,
SmallVector<Requirement, 2> additionalRequirements,
SmallVector<TypeLoc, 2> inferenceSources) {
assert(genericParamList && "Missing generic parameters?");
if (auto genericParamList = paramSource.dyn_cast<GenericParamList *>())
assert(genericParamList && "Missing generic parameters?");

auto request = InferredGenericSignatureRequest{
dc->getParentModule(), parentSig.getPointer(), genericParamList,
dc->getParentModule(), parentSig.getPointer(), paramSource,
additionalRequirements, inferenceSources,
allowConcreteGenericParams};
auto sig = evaluateOrDefault(dc->getASTContext().evaluator,
Expand Down Expand Up @@ -489,7 +490,7 @@ GenericSignature TypeChecker::checkGenericSignature(
/// extension's list of generic parameters.
static Type formExtensionInterfaceType(
ExtensionDecl *ext, Type type,
GenericParamList *genericParams,
const GenericParamList *genericParams,
SmallVectorImpl<Requirement> &sameTypeReqs,
bool &mustInferRequirements) {
if (type->is<ErrorType>())
Expand Down Expand Up @@ -602,28 +603,45 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
return sig;
}

// We can fast-path computing the generic signature of non-generic
// declarations by re-using the parent context's signature.
auto *gp = GC->getGenericParams();
if (!gp) {
return GC->getParent()->getGenericSignatureOfContext();
}
bool allowConcreteGenericParams = false;
const auto *genericParams = GC->getGenericParams();
if (genericParams) {
// Setup the depth of the generic parameters.
const_cast<GenericParamList *>(genericParams)
->setDepth(GC->getGenericContextDepth());

// Accessors can always use the generic context of their storage
// declarations. This is a compile-time optimization since it lets us
// avoid the requirements-gathering phase, but it also simplifies that
// work for accessors which don't mention the value type in their formal
// signatures (like the read and modify coroutines, since yield types
// aren't tracked in the AST type yet).
if (auto accessor = dyn_cast<AccessorDecl>(GC->getAsDecl())) {
return cast<SubscriptDecl>(accessor->getStorage())->getGenericSignature();
}

// ...or we may have a where clause dependent on outer generic parameters.
} else if (const auto *where = GC->getTrailingWhereClause()) {
// If there is no generic context for the where clause to
// rely on, diagnose that now and bail out.
if (!GC->isGenericContext()) {
GC->getASTContext().Diags.diagnose(where->getWhereLoc(),
diag::where_nongeneric_ctx);
return nullptr;
}

// Setup the depth of the generic parameters.
gp->setDepth(GC->getGenericContextDepth());

// Accessors can always use the generic context of their storage
// declarations. This is a compile-time optimization since it lets us
// avoid the requirements-gathering phase, but it also simplifies that
// work for accessors which don't mention the value type in their formal
// signatures (like the read and modify coroutines, since yield types
// aren't tracked in the AST type yet).
if (auto accessor = dyn_cast<AccessorDecl>(GC->getAsDecl())) {
return cast<SubscriptDecl>(accessor->getStorage())->getGenericSignature();
allowConcreteGenericParams = true;
} else {
// We can fast-path computing the generic signature of non-generic
// declarations by re-using the parent context's signature.
if (auto accessor = dyn_cast<AccessorDecl>(GC->getAsDecl()))
if (auto subscript = dyn_cast<SubscriptDecl>(accessor->getStorage()))
return subscript->getGenericSignature();

return GC->getParent()->getGenericSignatureOfContext();
}

auto parentSig = GC->getParent()->getGenericSignatureOfContext();
bool allowConcreteGenericParams = false;
SmallVector<TypeLoc, 2> inferenceSources;
SmallVector<Requirement, 2> sameTypeReqs;
if (auto VD = dyn_cast_or_null<ValueDecl>(GC->getAsDecl())) {
Expand Down Expand Up @@ -685,11 +703,11 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
bool mustInferRequirements = false;
Type extInterfaceType =
formExtensionInterfaceType(ext, ext->getExtendedType(),
gp, sameTypeReqs,
genericParams, sameTypeReqs,
mustInferRequirements);

auto cannotReuseNominalSignature = [&]() -> bool {
const auto finalDepth = gp->getParams().back()->getDepth();
const auto finalDepth = genericParams->getParams().back()->getDepth();
return mustInferRequirements
|| !sameTypeReqs.empty()
|| ext->getTrailingWhereClause()
Expand Down Expand Up @@ -717,7 +735,7 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
}

return TypeChecker::checkGenericSignature(
gp, GC, parentSig,
GC, GC, parentSig,
allowConcreteGenericParams,
sameTypeReqs, inferenceSources);
}
Expand Down
Loading

0 comments on commit 6f08216

Please sign in to comment.