mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
There's a group of methods in `DeclContext` with names that start with *is*, such as `isClassOrClassExtensionContext()`. These names suggests a boolean return value, while the methods actually return a type declaration. This patch replaces the *is* prefix with *getAs* to better reflect their interface.
421 lines
16 KiB
C++
421 lines
16 KiB
C++
//===--- DerivedConformanceRawRepresentable.cpp - Derived RawRepresentable ===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements implicit derivation of the RawRepresentable protocol
|
|
// for an enum.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "TypeChecker.h"
|
|
#include "swift/AST/ArchetypeBuilder.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/Stmt.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "DerivedConformances.h"
|
|
|
|
using namespace swift;
|
|
using namespace DerivedConformance;
|
|
|
|
static LiteralExpr *cloneRawLiteralExpr(ASTContext &C, LiteralExpr *expr) {
|
|
LiteralExpr *clone;
|
|
if (auto intLit = dyn_cast<IntegerLiteralExpr>(expr)) {
|
|
clone = new (C) IntegerLiteralExpr(intLit->getDigitsText(), expr->getLoc(),
|
|
/*implicit*/ true);
|
|
if (intLit->isNegative())
|
|
cast<IntegerLiteralExpr>(clone)->setNegative(expr->getLoc());
|
|
} else if (isa<NilLiteralExpr>(expr)) {
|
|
clone = new (C) NilLiteralExpr(expr->getLoc());
|
|
} else if (auto stringLit = dyn_cast<StringLiteralExpr>(expr)) {
|
|
clone = new (C) StringLiteralExpr(stringLit->getValue(), expr->getLoc());
|
|
} else if (auto floatLit = dyn_cast<FloatLiteralExpr>(expr)) {
|
|
clone = new (C) FloatLiteralExpr(floatLit->getDigitsText(), expr->getLoc(),
|
|
/*implicit*/ true);
|
|
if (floatLit->isNegative())
|
|
cast<FloatLiteralExpr>(clone)->setNegative(expr->getLoc());
|
|
} else {
|
|
llvm_unreachable("invalid raw literal expr");
|
|
}
|
|
clone->setImplicit();
|
|
return clone;
|
|
}
|
|
|
|
static Type deriveRawRepresentable_Raw(TypeChecker &tc, Decl *parentDecl,
|
|
EnumDecl *enumDecl) {
|
|
// enum SomeEnum : SomeType {
|
|
// @derived
|
|
// typealias Raw = SomeType
|
|
// }
|
|
auto rawInterfaceType = enumDecl->getRawType();
|
|
return ArchetypeBuilder::mapTypeIntoContext(cast<DeclContext>(parentDecl),
|
|
rawInterfaceType);
|
|
}
|
|
|
|
static void deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl) {
|
|
// enum SomeEnum : SomeType {
|
|
// case A = 111, B = 222
|
|
// @derived
|
|
// var raw: SomeType {
|
|
// switch self {
|
|
// case A:
|
|
// return 111
|
|
// case B:
|
|
// return 222
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
auto parentDC = toRawDecl->getDeclContext();
|
|
ASTContext &C = parentDC->getASTContext();
|
|
|
|
auto enumDecl = parentDC->getAsEnumOrEnumExtensionContext();
|
|
|
|
Type rawTy = enumDecl->getRawType();
|
|
assert(rawTy);
|
|
for (auto elt : enumDecl->getAllElements()) {
|
|
if (!elt->getTypeCheckedRawValueExpr() ||
|
|
!elt->getTypeCheckedRawValueExpr()->getType()->isEqual(rawTy)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
Type enumType = parentDC->getDeclaredTypeInContext();
|
|
|
|
SmallVector<CaseStmt*, 4> cases;
|
|
for (auto elt : enumDecl->getAllElements()) {
|
|
auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType),
|
|
SourceLoc(), SourceLoc(),
|
|
Identifier(), elt, nullptr);
|
|
pat->setImplicit();
|
|
|
|
auto labelItem =
|
|
CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), nullptr);
|
|
|
|
auto returnExpr = cloneRawLiteralExpr(C, elt->getRawValueExpr());
|
|
auto returnStmt = new (C) ReturnStmt(SourceLoc(), returnExpr);
|
|
|
|
auto body = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(returnStmt), SourceLoc());
|
|
|
|
cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem,
|
|
/*HasBoundDecls=*/false, SourceLoc(),
|
|
body));
|
|
}
|
|
|
|
auto selfRef = createSelfDeclRef(toRawDecl);
|
|
auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), selfRef,
|
|
SourceLoc(), cases, SourceLoc(), C);
|
|
auto body = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(switchStmt),
|
|
SourceLoc());
|
|
toRawDecl->setBody(body);
|
|
}
|
|
|
|
static VarDecl *deriveRawRepresentable_raw(TypeChecker &tc,
|
|
Decl *parentDecl,
|
|
EnumDecl *enumDecl) {
|
|
ASTContext &C = tc.Context;
|
|
|
|
auto parentDC = cast<DeclContext>(parentDecl);
|
|
auto rawInterfaceType = enumDecl->getRawType();
|
|
auto rawType = ArchetypeBuilder::mapTypeIntoContext(parentDC,
|
|
rawInterfaceType);
|
|
// Define the getter.
|
|
auto getterDecl = declareDerivedPropertyGetter(tc, parentDecl, enumDecl,
|
|
rawInterfaceType,
|
|
rawType);
|
|
getterDecl->setBodySynthesizer(&deriveBodyRawRepresentable_raw);
|
|
|
|
// Define the property.
|
|
VarDecl *propDecl;
|
|
PatternBindingDecl *pbDecl;
|
|
std::tie(propDecl, pbDecl)
|
|
= declareDerivedReadOnlyProperty(tc, parentDecl, enumDecl,
|
|
C.Id_rawValue, rawType,
|
|
rawInterfaceType,
|
|
getterDecl);
|
|
|
|
auto dc = cast<IterableDeclContext>(parentDecl);
|
|
dc->addMember(getterDecl);
|
|
dc->addMember(propDecl);
|
|
dc->addMember(pbDecl);
|
|
|
|
return propDecl;
|
|
}
|
|
|
|
static void
|
|
deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl) {
|
|
// enum SomeEnum : SomeType {
|
|
// case A = 111, B = 222
|
|
// @derived
|
|
// init?(rawValue: SomeType) {
|
|
// switch rawValue {
|
|
// case 111:
|
|
// self = .A
|
|
// case 222:
|
|
// self = .B
|
|
// default:
|
|
// return nil
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
auto parentDC = initDecl->getDeclContext();
|
|
ASTContext &C = parentDC->getASTContext();
|
|
|
|
auto nominalTypeDecl = parentDC->getAsNominalTypeOrNominalTypeExtensionContext();
|
|
auto enumDecl = cast<EnumDecl>(nominalTypeDecl);
|
|
|
|
Type rawTy = enumDecl->getRawType();
|
|
assert(rawTy);
|
|
rawTy = ArchetypeBuilder::mapTypeIntoContext(initDecl, rawTy);
|
|
|
|
for (auto elt : enumDecl->getAllElements()) {
|
|
if (!elt->getTypeCheckedRawValueExpr() ||
|
|
!elt->getTypeCheckedRawValueExpr()->getType()->isEqual(rawTy)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
Type enumType = parentDC->getDeclaredTypeInContext();
|
|
|
|
auto selfDecl = cast<ConstructorDecl>(initDecl)->getImplicitSelfDecl();
|
|
|
|
SmallVector<CaseStmt*, 4> cases;
|
|
for (auto elt : enumDecl->getAllElements()) {
|
|
auto litExpr = cloneRawLiteralExpr(C, elt->getRawValueExpr());
|
|
auto litPat = new (C) ExprPattern(litExpr, /*isResolved*/ true,
|
|
nullptr, nullptr);
|
|
litPat->setImplicit();
|
|
|
|
auto labelItem =
|
|
CaseLabelItem(/*IsDefault=*/false, litPat, SourceLoc(), nullptr);
|
|
|
|
auto eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*implicit*/true);
|
|
auto metaTyRef = TypeExpr::createImplicit(enumType, C);
|
|
auto valueExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef);
|
|
|
|
auto selfRef = new (C) DeclRefExpr(selfDecl, DeclNameLoc(),
|
|
/*implicit*/true,
|
|
AccessSemantics::DirectToStorage);
|
|
|
|
auto assignment = new (C) AssignExpr(selfRef, SourceLoc(), valueExpr,
|
|
/*implicit*/ true);
|
|
|
|
auto body = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(assignment), SourceLoc());
|
|
|
|
cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem,
|
|
/*HasBoundDecls=*/false, SourceLoc(),
|
|
body));
|
|
}
|
|
|
|
auto anyPat = new (C) AnyPattern(SourceLoc());
|
|
anyPat->setImplicit();
|
|
auto dfltLabelItem =
|
|
CaseLabelItem(/*IsDefault=*/true, anyPat, SourceLoc(), nullptr);
|
|
|
|
auto dfltReturnStmt = new (C) FailStmt(SourceLoc(), SourceLoc());
|
|
auto dfltBody = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(dfltReturnStmt), SourceLoc());
|
|
cases.push_back(CaseStmt::create(C, SourceLoc(), dfltLabelItem,
|
|
/*HasBoundDecls=*/false, SourceLoc(),
|
|
dfltBody));
|
|
|
|
auto rawDecl = initDecl->getParameterList(1)->get(0);
|
|
auto rawRef = new (C) DeclRefExpr(rawDecl, DeclNameLoc(), /*implicit*/true);
|
|
auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), rawRef,
|
|
SourceLoc(), cases, SourceLoc(), C);
|
|
auto body = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(switchStmt),
|
|
SourceLoc());
|
|
initDecl->setBody(body);
|
|
}
|
|
|
|
static ConstructorDecl *deriveRawRepresentable_init(TypeChecker &tc,
|
|
Decl *parentDecl,
|
|
EnumDecl *enumDecl) {
|
|
ASTContext &C = tc.Context;
|
|
|
|
auto parentDC = cast<DeclContext>(parentDecl);
|
|
auto rawInterfaceType = enumDecl->getRawType();
|
|
auto rawType = ArchetypeBuilder::mapTypeIntoContext(parentDC,
|
|
rawInterfaceType);
|
|
|
|
// Make sure that the raw type is Equatable. We need it to ensure that we have
|
|
// a suitable ~= for the switch.
|
|
auto equatableProto = tc.getProtocol(enumDecl->getLoc(),
|
|
KnownProtocolKind::Equatable);
|
|
if (!equatableProto)
|
|
return nullptr;
|
|
|
|
if (!tc.conformsToProtocol(rawType, equatableProto, enumDecl, None)) {
|
|
SourceLoc loc = enumDecl->getInherited()[0].getSourceRange().Start;
|
|
tc.diagnose(loc, diag::enum_raw_type_not_equatable, rawType);
|
|
return nullptr;
|
|
}
|
|
|
|
Type enumType = parentDC->getDeclaredTypeInContext();
|
|
auto *selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC,
|
|
/*static*/false, /*inout*/true);
|
|
|
|
auto *rawDecl = new (C) ParamDecl(/*IsLet*/true, SourceLoc(), SourceLoc(),
|
|
C.Id_rawValue, SourceLoc(),
|
|
C.Id_rawValue, rawType, parentDC);
|
|
rawDecl->setImplicit();
|
|
auto paramList = ParameterList::createWithoutLoc(rawDecl);
|
|
|
|
auto retTy = OptionalType::get(enumType);
|
|
DeclName name(C, C.Id_init, paramList);
|
|
|
|
auto initDecl = new (C) ConstructorDecl(name, SourceLoc(),
|
|
/*failability*/ OTK_Optional,
|
|
SourceLoc(), selfDecl, paramList,
|
|
nullptr, SourceLoc(), parentDC);
|
|
|
|
initDecl->setImplicit();
|
|
initDecl->setBodySynthesizer(&deriveBodyRawRepresentable_init);
|
|
|
|
// Compute the type of the initializer.
|
|
GenericParamList *genericParams = initDecl->getGenericParamsOfContext();
|
|
|
|
TupleTypeElt element(rawType, C.Id_rawValue);
|
|
auto argType = TupleType::get(element, C);
|
|
TupleTypeElt interfaceElement(rawInterfaceType, C.Id_rawValue);
|
|
auto interfaceArgType = TupleType::get(interfaceElement, C);
|
|
|
|
Type type = FunctionType::get(argType, retTy);
|
|
|
|
Type selfType = initDecl->computeSelfType();
|
|
selfDecl->overwriteType(selfType);
|
|
Type selfMetatype = MetatypeType::get(selfType->getInOutObjectType());
|
|
|
|
Type allocType;
|
|
Type initType;
|
|
if (genericParams) {
|
|
allocType = PolymorphicFunctionType::get(selfMetatype, type, genericParams);
|
|
initType = PolymorphicFunctionType::get(selfType, type, genericParams);
|
|
} else {
|
|
allocType = FunctionType::get(selfMetatype, type);
|
|
initType = FunctionType::get(selfType, type);
|
|
}
|
|
initDecl->setType(allocType);
|
|
initDecl->setInitializerType(initType);
|
|
|
|
// Compute the interface type of the initializer.
|
|
Type retInterfaceType
|
|
= OptionalType::get(parentDC->getDeclaredInterfaceType());
|
|
Type interfaceType = FunctionType::get(interfaceArgType, retInterfaceType);
|
|
Type selfInterfaceType = initDecl->computeInterfaceSelfType(/*init*/ false);
|
|
Type selfInitializerInterfaceType
|
|
= initDecl->computeInterfaceSelfType(/*init*/ true);
|
|
|
|
Type allocIfaceType;
|
|
Type initIfaceType;
|
|
if (auto sig = parentDC->getGenericSignatureOfContext()) {
|
|
allocIfaceType = GenericFunctionType::get(sig, selfInterfaceType,
|
|
interfaceType,
|
|
FunctionType::ExtInfo());
|
|
initIfaceType = GenericFunctionType::get(sig, selfInitializerInterfaceType,
|
|
interfaceType,
|
|
FunctionType::ExtInfo());
|
|
} else {
|
|
allocIfaceType = FunctionType::get(selfMetatype, type);
|
|
initIfaceType = FunctionType::get(selfType, type);
|
|
}
|
|
initDecl->setInterfaceType(allocIfaceType);
|
|
initDecl->setInitializerInterfaceType(initIfaceType);
|
|
initDecl->setAccessibility(enumDecl->getFormalAccess());
|
|
|
|
// If the enum was not imported, the derived conformance is either from the
|
|
// enum itself or an extension, in which case we will emit the declaration
|
|
// normally.
|
|
if (enumDecl->hasClangNode())
|
|
tc.Context.addExternalDecl(initDecl);
|
|
|
|
cast<IterableDeclContext>(parentDecl)->addMember(initDecl);
|
|
return initDecl;
|
|
}
|
|
|
|
ValueDecl *DerivedConformance::deriveRawRepresentable(TypeChecker &tc,
|
|
Decl *parentDecl,
|
|
NominalTypeDecl *type,
|
|
ValueDecl *requirement) {
|
|
// Check preconditions. These should already have been diagnosed by
|
|
// type-checking but we may still get here after recovery.
|
|
|
|
// The type must be an enum.
|
|
auto enumDecl = dyn_cast<EnumDecl>(type);
|
|
if (!enumDecl)
|
|
return nullptr;
|
|
|
|
// It must have a valid raw type.
|
|
if (!enumDecl->hasRawType())
|
|
return nullptr;
|
|
if (!enumDecl->getInherited().empty() &&
|
|
enumDecl->getInherited().front().isError())
|
|
return nullptr;
|
|
|
|
// There must be enum elements.
|
|
if (enumDecl->getAllElements().empty())
|
|
return nullptr;
|
|
|
|
for (auto elt : enumDecl->getAllElements())
|
|
tc.validateDecl(elt);
|
|
|
|
if (requirement->getName() == tc.Context.Id_rawValue)
|
|
return deriveRawRepresentable_raw(tc, parentDecl, enumDecl);
|
|
|
|
if (requirement->getName() == tc.Context.Id_init)
|
|
return deriveRawRepresentable_init(tc, parentDecl, enumDecl);
|
|
|
|
tc.diagnose(requirement->getLoc(),
|
|
diag::broken_raw_representable_requirement);
|
|
return nullptr;
|
|
}
|
|
|
|
Type DerivedConformance::deriveRawRepresentable(TypeChecker &tc,
|
|
Decl *parentDecl,
|
|
NominalTypeDecl *type,
|
|
AssociatedTypeDecl *assocType) {
|
|
// Check preconditions. These should already have been diagnosed by
|
|
// type-checking but we may still get here after recovery.
|
|
|
|
// The type must be an enum.
|
|
auto enumDecl = dyn_cast<EnumDecl>(type);
|
|
if (!enumDecl)
|
|
return nullptr;
|
|
|
|
// It must have a valid raw type.
|
|
if (!enumDecl->hasRawType())
|
|
return nullptr;
|
|
if (!enumDecl->getInherited().empty() &&
|
|
enumDecl->getInherited().front().isError())
|
|
return nullptr;
|
|
|
|
// There must be enum elements.
|
|
if (enumDecl->getAllElements().empty())
|
|
return nullptr;
|
|
|
|
for (auto elt : enumDecl->getAllElements())
|
|
tc.validateDecl(elt);
|
|
|
|
if (assocType->getName() == tc.Context.Id_RawValue) {
|
|
return deriveRawRepresentable_Raw(tc, parentDecl, enumDecl);
|
|
}
|
|
|
|
tc.diagnose(assocType->getLoc(),
|
|
diag::broken_raw_representable_requirement);
|
|
return nullptr;
|
|
}
|