blob: 06c4e510dd7cf6a63609d07a59fab2dc81660d5f [file] [log] [blame]
//===--- TypeCheckRequests.cpp - Type Checking Requests ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "TypeChecker.h"
#include "TypeCheckType.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/Attr.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/Decl.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeLoc.h"
#include "swift/AST/Types.h"
#include "swift/Subsystems.h"
using namespace swift;
Type InheritedTypeRequest::evaluate(
Evaluator &evaluator,
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
unsigned index, TypeResolutionStage stage) const {
// Figure out how to resolve types.
DeclContext *dc;
if (auto typeDecl = decl.dyn_cast<const TypeDecl *>()) {
if (auto nominal = dyn_cast<NominalTypeDecl>(typeDecl)) {
dc = (DeclContext *)nominal;
} else {
dc = typeDecl->getDeclContext();
}
} else {
dc = (DeclContext *)decl.get<const ExtensionDecl *>();
}
Optional<TypeResolution> resolution;
switch (stage) {
case TypeResolutionStage::Structural:
resolution =
TypeResolution::forStructural(dc, None, /*unboundTyOpener*/ nullptr);
break;
case TypeResolutionStage::Interface:
resolution =
TypeResolution::forInterface(dc, None, /*unboundTyOpener*/ nullptr);
break;
case TypeResolutionStage::Contextual: {
// Compute the contextual type by mapping the interface type into context.
auto result =
evaluator(InheritedTypeRequest{decl, index,
TypeResolutionStage::Interface});
if (!result)
return Type();
return dc->mapTypeIntoContext(*result);
}
}
const TypeLoc &typeLoc = getInheritedTypeLocAtIndex(decl, index);
Type inheritedType;
if (typeLoc.getTypeRepr())
inheritedType = resolution->resolveType(typeLoc.getTypeRepr());
else
inheritedType = typeLoc.getType();
return inheritedType ? inheritedType : ErrorType::get(dc->getASTContext());
}
Type
SuperclassTypeRequest::evaluate(Evaluator &evaluator,
NominalTypeDecl *nominalDecl,
TypeResolutionStage stage) const {
assert(isa<ClassDecl>(nominalDecl) || isa<ProtocolDecl>(nominalDecl));
// If this is a protocol that came from a serialized module, compute the
// superclass via its generic signature.
if (auto *proto = dyn_cast<ProtocolDecl>(nominalDecl)) {
if (proto->wasDeserialized()) {
return proto->getGenericSignature()
->getSuperclassBound(proto->getSelfInterfaceType());
}
if (!proto->getSuperclassDecl())
return Type();
} else if (auto classDecl = dyn_cast<ClassDecl>(nominalDecl)) {
if (!classDecl->getSuperclassDecl())
return Type();
}
for (unsigned int idx : indices(nominalDecl->getInherited())) {
auto result = evaluator(InheritedTypeRequest{nominalDecl, idx, stage});
if (auto err = result.takeError()) {
// FIXME: Should this just return once a cycle is detected?
llvm::handleAllErrors(std::move(err),
[](const CyclicalRequestError<InheritedTypeRequest> &E) {
/* cycle detected */
});
continue;
}
Type inheritedType = *result;
if (!inheritedType) continue;
// If we found a class, return it.
if (inheritedType->getClassOrBoundGenericClass()) {
return inheritedType;
}
// If we found an existential with a superclass bound, return it.
if (inheritedType->isExistentialType()) {
if (auto superclassType =
inheritedType->getExistentialLayout().explicitSuperclass) {
if (superclassType->getClassOrBoundGenericClass()) {
return superclassType;
}
}
}
}
// No superclass.
return Type();
}
Type EnumRawTypeRequest::evaluate(Evaluator &evaluator,
EnumDecl *enumDecl) const {
for (unsigned int idx : indices(enumDecl->getInherited())) {
auto inheritedTypeResult = evaluator(
InheritedTypeRequest{enumDecl, idx, TypeResolutionStage::Interface});
if (auto err = inheritedTypeResult.takeError()) {
llvm::handleAllErrors(std::move(err),
[](const CyclicalRequestError<InheritedTypeRequest> &E) {
// cycle detected
});
continue;
}
auto &inheritedType = *inheritedTypeResult;
if (!inheritedType) continue;
// Skip existential types.
if (inheritedType->isExistentialType()) continue;
// We found a raw type; return it.
return inheritedType;
}
// No raw type.
return Type();
}
CustomAttr *
AttachedResultBuilderRequest::evaluate(Evaluator &evaluator,
ValueDecl *decl) const {
ASTContext &ctx = decl->getASTContext();
auto dc = decl->getDeclContext();
for (auto attr : decl->getAttrs().getAttributes<CustomAttr>()) {
auto mutableAttr = const_cast<CustomAttr *>(attr);
// Figure out which nominal declaration this custom attribute refers to.
auto nominal = evaluateOrDefault(ctx.evaluator,
CustomAttrNominalRequest{mutableAttr, dc},
nullptr);
// Ignore unresolvable custom attributes.
if (!nominal)
continue;
// Return the first custom attribute that is a result builder type.
if (nominal->getAttrs().hasAttribute<ResultBuilderAttr>())
return mutableAttr;
}
return nullptr;
}
/// Attempt to infer the result builder type for a declaration.
static Type inferResultBuilderType(ValueDecl *decl) {
auto dc = decl->getDeclContext();
if (!dc->isTypeContext() || isa<ProtocolDecl>(dc))
return Type();
auto funcDecl = dyn_cast<FuncDecl>(decl);
if (!funcDecl || !funcDecl->hasBody() ||
!decl->getDeclContext()->getParentSourceFile())
return Type();
// Check whether there are any return statements in the function's body.
// If there are, the result builder transform will be disabled,
// so don't infer a result builder.
if (!TypeChecker::findReturnStatements(funcDecl).empty())
return Type();
// Only getters can have result builders. When we find one, look at
// the storage declaration for the purposes of witness matching.
auto lookupDecl = decl;
if (auto accessor = dyn_cast<AccessorDecl>(funcDecl)) {
if (accessor->getAccessorKind() != AccessorKind::Get)
return Type();
lookupDecl = accessor->getStorage();
}
// Find all of the potentially inferred result builder types.
struct Match {
enum Kind {
Conformance,
DynamicReplacement,
} kind;
union {
struct {
ProtocolConformance *conformance;
ValueDecl *requirement;
} conformanceMatch;
ValueDecl *dynamicReplacement;
};
Type resultBuilderType;
static Match forConformance(
ProtocolConformance *conformance,
ValueDecl *requirement,
Type resultBuilderType) {
Match match;
match.kind = Conformance;
match.conformanceMatch.conformance = conformance;
match.conformanceMatch.requirement = requirement;
match.resultBuilderType = resultBuilderType;
return match;
}
static Match forDynamicReplacement(
ValueDecl *dynamicReplacement, Type resultBuilderType) {
Match match;
match.kind = DynamicReplacement;
match.dynamicReplacement = dynamicReplacement;
match.resultBuilderType = resultBuilderType;
return match;
}
DeclName getSourceName() const {
switch (kind) {
case Conformance:
return conformanceMatch.conformance->getProtocol()->getName();
case DynamicReplacement:
return dynamicReplacement->getName();
}
llvm_unreachable("unhandled decl name kind!");
}
};
// The set of matches from which we can infer result builder types.
SmallVector<Match, 2> matches;
// Determine all of the conformances within the same context as
// this declaration. If this declaration is a witness to any
// requirement within one of those protocols that has a result builder
// attached, use that result builder type.
auto addConformanceMatches = [&matches](ValueDecl *lookupDecl) {
DeclContext *dc = lookupDecl->getDeclContext();
auto idc = cast<IterableDeclContext>(dc->getAsDecl());
auto conformances = evaluateOrDefault(
dc->getASTContext().evaluator,
LookupAllConformancesInContextRequest{idc}, { });
for (auto conformance : conformances) {
auto protocol = conformance->getProtocol();
for (auto found : protocol->lookupDirect(lookupDecl->getName())) {
if (!isa<ProtocolDecl>(found->getDeclContext()))
continue;
auto requirement = dyn_cast<ValueDecl>(found);
if (!requirement)
continue;
Type resultBuilderType = requirement->getResultBuilderType();
if (!resultBuilderType)
continue;
auto witness = conformance->getWitnessDecl(requirement);
if (witness != lookupDecl)
continue;
// Substitute into the result builder type.
auto subs =
conformance->getSubstitutions(lookupDecl->getModuleContext());
Type subResultBuilderType = resultBuilderType.subst(subs);
matches.push_back(
Match::forConformance(
conformance, requirement, subResultBuilderType));
}
}
};
addConformanceMatches(lookupDecl);
// Look for result builder types inferred through dynamic replacements.
if (auto replaced = lookupDecl->getDynamicallyReplacedDecl()) {
if (auto resultBuilderType = replaced->getResultBuilderType()) {
matches.push_back(
Match::forDynamicReplacement(replaced, resultBuilderType));
} else {
addConformanceMatches(replaced);
}
}
if (matches.size() == 0)
return Type();
// Determine whether there is more than one actual result builder type.
Type resultBuilderType = matches[0].resultBuilderType;
for (const auto &match : matches) {
// If the types were the same anyway, there's nothing to do.
Type otherResultBuilderType = match.resultBuilderType;
if (resultBuilderType->isEqual(otherResultBuilderType))
continue;
// We have at least two different result builder types.
// Diagnose the ambiguity and provide potential solutions.
decl->diagnose(
diag::result_builder_infer_ambig, lookupDecl->getName(),
resultBuilderType, otherResultBuilderType);
decl->diagnose(diag::result_builder_infer_add_return)
.fixItInsert(funcDecl->getBodySourceRange().End, "return <#expr#>\n");
for (const auto &match : matches) {
decl->diagnose(
diag::result_builder_infer_pick_specific,
match.resultBuilderType,
static_cast<unsigned>(match.kind),
match.getSourceName())
.fixItInsert(
lookupDecl->getAttributeInsertionLoc(false),
"@" + match.resultBuilderType.getString() + " ");
}
return Type();
}
return resultBuilderType;
}
Type ResultBuilderTypeRequest::evaluate(Evaluator &evaluator,
ValueDecl *decl) const {
// Look for a result-builder custom attribute.
auto attr = decl->getAttachedResultBuilder();
if (!attr)
return inferResultBuilderType(decl);
// Resolve a type for the attribute.
auto mutableAttr = const_cast<CustomAttr*>(attr);
auto dc = decl->getDeclContext();
auto &ctx = dc->getASTContext();
Type type = evaluateOrDefault(
evaluator,
CustomAttrTypeRequest{mutableAttr, dc, CustomAttrTypeKind::NonGeneric},
Type());
if (!type || type->hasError()) return Type();
auto nominal = type->getAnyNominal();
if (!nominal) {
assert(ctx.Diags.hadAnyError());
return Type();
}
// Do some additional checking on parameters.
if (auto param = dyn_cast<ParamDecl>(decl)) {
// The parameter had better already have an interface type.
Type paramType = param->getInterfaceType();
assert(paramType);
auto paramFnType = paramType->getAs<FunctionType>();
// Require the parameter to be an interface type.
if (!paramFnType) {
ctx.Diags.diagnose(attr->getLocation(),
diag::result_builder_parameter_not_of_function_type,
nominal->getName());
mutableAttr->setInvalid();
return Type();
}
// Forbid the parameter to be an autoclosure.
if (param->isAutoClosure()) {
ctx.Diags.diagnose(attr->getLocation(),
diag::result_builder_parameter_autoclosure,
nominal->getName());
mutableAttr->setInvalid();
return Type();
}
}
return type->mapTypeOutOfContext();
}
// Define request evaluation functions for each of the type checker requests.
static AbstractRequestFunction *typeCheckerRequestFunctions[] = {
#define SWIFT_REQUEST(Zone, Name, Sig, Caching, LocOptions) \
reinterpret_cast<AbstractRequestFunction *>(&Name::evaluateRequest),
#include "swift/AST/TypeCheckerTypeIDZone.def"
#undef SWIFT_REQUEST
};
void swift::registerTypeCheckerRequestFunctions(Evaluator &evaluator) {
evaluator.registerRequestFunctions(Zone::TypeChecker,
typeCheckerRequestFunctions);
}