mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
More detail: some members are intended to have the same the access as their containing types. This doesn't fly for SE-0025 'private', which would limit the members to only being accessed from lexically within the type decl, instead anywhere the type itself can be seen. Instead, follow the rule for user-written members---internal by default---and then raise the access level to 'public' if necessary. This affects: - enum cases - deinitializers - protocol requirements - generic parameters - implicit initializers - inherited initializers - derived conformance members - synthesized typealiases for associated types
423 lines
16 KiB
C++
423 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,
|
|
rawInterfaceType,
|
|
rawType,
|
|
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::createUnboundSelf(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,
|
|
/*FailabilityLoc=*/SourceLoc(),
|
|
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
|
selfDecl, paramList,
|
|
/*GenericParams=*/nullptr, 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;
|
|
if (genericParams)
|
|
allocType = PolymorphicFunctionType::get(selfMetatype, type, genericParams);
|
|
else
|
|
allocType = FunctionType::get(selfMetatype, type);
|
|
initDecl->setType(allocType);
|
|
|
|
// 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()) {
|
|
initDecl->setGenericSignature(sig);
|
|
|
|
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(std::max(Accessibility::Internal,
|
|
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;
|
|
}
|