mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
543 lines
20 KiB
C++
543 lines
20 KiB
C++
//===--- DerivedConformanceRawRepresentable.cpp - Derived RawRepresentable ===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements implicit derivation of the RawRepresentable protocol
|
|
// for an enum.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CodeSynthesis.h"
|
|
#include "DerivedConformances.h"
|
|
#include "TypeCheckAvailability.h"
|
|
#include "TypeCheckDecl.h"
|
|
#include "TypeChecker.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/Stmt.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
|
|
using namespace swift;
|
|
|
|
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 if (auto boolLit = dyn_cast<BooleanLiteralExpr>(expr)) {
|
|
clone = new (C) BooleanLiteralExpr(boolLit->getValue(), expr->getLoc(),
|
|
/*implicit*/true);
|
|
} else {
|
|
llvm_unreachable("invalid raw literal expr");
|
|
}
|
|
clone->setImplicit();
|
|
return clone;
|
|
}
|
|
|
|
static Type deriveRawRepresentable_Raw(DerivedConformance &derived) {
|
|
// enum SomeEnum : SomeType {
|
|
// @derived
|
|
// typealias Raw = SomeType
|
|
// }
|
|
auto rawInterfaceType = cast<EnumDecl>(derived.Nominal)->getRawType();
|
|
return derived.getConformanceContext()->mapTypeIntoContext(rawInterfaceType);
|
|
}
|
|
|
|
static std::pair<BraceStmt *, bool>
|
|
deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl, void *) {
|
|
// 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->getSelfEnumDecl();
|
|
|
|
Type rawTy = enumDecl->getRawType();
|
|
assert(rawTy);
|
|
rawTy = toRawDecl->mapTypeIntoContext(rawTy);
|
|
|
|
if (enumDecl->isObjC()) {
|
|
// Special case: ObjC enums are represented by their raw value, so just use
|
|
// a bitcast.
|
|
|
|
// return unsafeBitCast(self, to: RawType.self)
|
|
auto functionRef = UnresolvedDeclRefExpr::createImplicit(
|
|
C, C.getIdentifier("unsafeBitCast"), {Identifier(), C.Id_to});
|
|
auto selfRef = DerivedConformance::createSelfDeclRef(toRawDecl);
|
|
auto bareTypeExpr = TypeExpr::createImplicit(rawTy, C);
|
|
auto typeExpr = new (C) DotSelfExpr(bareTypeExpr, SourceLoc(), SourceLoc());
|
|
|
|
auto *argList = ArgumentList::forImplicitCallTo(functionRef->getName(),
|
|
{selfRef, typeExpr}, C);
|
|
auto call = CallExpr::createImplicit(C, functionRef, argList);
|
|
auto *returnStmt = ReturnStmt::createImplicit(C, call);
|
|
auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt),
|
|
SourceLoc());
|
|
return { body, /*isTypeChecked=*/false };
|
|
}
|
|
|
|
Type enumType = parentDC->getDeclaredTypeInContext();
|
|
|
|
SmallVector<ASTNode, 4> cases;
|
|
for (auto elt : enumDecl->getAllElements()) {
|
|
auto *pat = EnumElementPattern::createImplicit(
|
|
enumType, elt, /*subPattern*/ nullptr, /*DC*/ toRawDecl);
|
|
|
|
auto labelItem = CaseLabelItem(pat);
|
|
|
|
auto returnExpr = cloneRawLiteralExpr(C, elt->getRawValueExpr());
|
|
auto *returnStmt = ReturnStmt::createImplicit(C, returnExpr);
|
|
|
|
auto body = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(returnStmt), SourceLoc());
|
|
|
|
cases.push_back(CaseStmt::create(C, CaseParentKind::Switch, SourceLoc(),
|
|
labelItem, SourceLoc(), SourceLoc(), body,
|
|
/*case body var decls*/ std::nullopt));
|
|
}
|
|
|
|
auto selfRef = DerivedConformance::createSelfDeclRef(toRawDecl);
|
|
auto switchStmt =
|
|
SwitchStmt::createImplicit(LabeledStmtInfo(), selfRef, cases, C);
|
|
auto body = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(switchStmt),
|
|
SourceLoc());
|
|
return { body, /*isTypeChecked=*/false };
|
|
}
|
|
|
|
static void maybeMarkAsInlinable(DerivedConformance &derived,
|
|
AbstractFunctionDecl *afd) {
|
|
ASTContext &C = derived.Context;
|
|
auto parentDC = derived.getConformanceContext();
|
|
if (!parentDC->getParentModule()->isResilient()) {
|
|
AccessScope access =
|
|
afd->getFormalAccessScope(nullptr,
|
|
/*treatUsableFromInlineAsPublic*/true);
|
|
if (auto *attr = afd->getAttrs().getAttribute<UsableFromInlineAttr>())
|
|
attr->setInvalid();
|
|
if (access.isPublic())
|
|
afd->getAttrs().add(new (C) InlinableAttr(/*implicit*/false));
|
|
}
|
|
}
|
|
|
|
static VarDecl *deriveRawRepresentable_raw(DerivedConformance &derived) {
|
|
ASTContext &C = derived.Context;
|
|
|
|
auto enumDecl = cast<EnumDecl>(derived.Nominal);
|
|
auto rawInterfaceType = enumDecl->getRawType();
|
|
|
|
// Define the property.
|
|
VarDecl *propDecl;
|
|
PatternBindingDecl *pbDecl;
|
|
std::tie(propDecl, pbDecl) = derived.declareDerivedProperty(
|
|
DerivedConformance::SynthesizedIntroducer::Var, C.Id_rawValue,
|
|
rawInterfaceType, /*isStatic=*/false, /*isFinal=*/false);
|
|
addNonIsolatedToSynthesized(enumDecl, propDecl);
|
|
|
|
// Define the getter.
|
|
auto getterDecl =
|
|
DerivedConformance::addGetterToReadOnlyDerivedProperty(propDecl);
|
|
getterDecl->setBodySynthesizer(&deriveBodyRawRepresentable_raw);
|
|
|
|
// If the containing module is not resilient, make sure clients can get at
|
|
// the raw value without function call overhead.
|
|
maybeMarkAsInlinable(derived, getterDecl);
|
|
|
|
derived.addMembersToConformanceContext({propDecl, pbDecl});
|
|
|
|
return propDecl;
|
|
}
|
|
|
|
/// Contains information needed to synthesize a runtime version check.
|
|
struct RuntimeVersionCheck {
|
|
PlatformKind Platform;
|
|
llvm::VersionTuple Version;
|
|
|
|
RuntimeVersionCheck(PlatformKind Platform, llvm::VersionTuple Version)
|
|
: Platform(Platform), Version(Version)
|
|
{ }
|
|
|
|
VersionRange getVersionRange() const {
|
|
return VersionRange::allGTE(Version);
|
|
}
|
|
|
|
/// Synthesizes a statement which returns nil if the runtime version check
|
|
/// fails, e.g. "guard #available(iOS 10, *) else { return nil }".
|
|
Stmt *createEarlyReturnStmt(ASTContext &C) const {
|
|
// platformSpec = "\(attr.platform) \(attr.introduced)"
|
|
auto platformSpec = new (C) PlatformVersionConstraintAvailabilitySpec(
|
|
Platform, SourceLoc(),
|
|
Version, Version, SourceLoc()
|
|
);
|
|
|
|
// otherSpec = "*"
|
|
auto otherSpec = new (C) OtherPlatformAvailabilitySpec(SourceLoc());
|
|
|
|
// availableInfo = "#available(\(platformSpec), \(otherSpec))"
|
|
auto availableInfo = PoundAvailableInfo::create(
|
|
C, SourceLoc(), SourceLoc(), { platformSpec, otherSpec }, SourceLoc(),
|
|
false);
|
|
|
|
// This won't be filled in by TypeCheckAvailability because we have
|
|
// invalid SourceLocs in this area of the AST.
|
|
availableInfo->setAvailableRange(getVersionRange());
|
|
|
|
// earlyReturnBody = "{ return nil }"
|
|
auto earlyReturn = new (C) FailStmt(SourceLoc(), SourceLoc());
|
|
auto earlyReturnBody = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(earlyReturn),
|
|
SourceLoc(), /*implicit=*/true);
|
|
|
|
// guardStmt = "guard \(availableInfo) else \(earlyReturnBody)"
|
|
StmtConditionElement conds[1] = { availableInfo };
|
|
auto guardStmt = new (C) GuardStmt(SourceLoc(), C.AllocateCopy(conds),
|
|
earlyReturnBody, /*implicit=*/true);
|
|
|
|
return guardStmt;
|
|
}
|
|
};
|
|
|
|
/// Checks if the case will be available at runtime given the current target
|
|
/// platform. If it will never be available, returns false. If it will always
|
|
/// be available, returns true. If it will sometimes be available, adds
|
|
/// information about the runtime check needed to ensure it is available to
|
|
/// \c versionCheck and returns true.
|
|
static bool
|
|
checkAvailability(const EnumElementDecl *elt,
|
|
AvailabilityContext availabilityContext,
|
|
std::optional<RuntimeVersionCheck> &versionCheck) {
|
|
auto &C = elt->getASTContext();
|
|
auto constraint =
|
|
getUnsatisfiedAvailabilityConstraint(elt, availabilityContext);
|
|
|
|
// Is it always available?
|
|
if (!constraint)
|
|
return true;
|
|
|
|
// Some constraints are active for type checking but can't translate to
|
|
// runtime restrictions.
|
|
if (!constraint->isActiveForRuntimeQueries(C))
|
|
return true;
|
|
|
|
// Is it never available?
|
|
if (!constraint->isConditionallySatisfiable())
|
|
return false;
|
|
|
|
// It's conditionally available; create a version constraint and return true.
|
|
auto platform = constraint->getPlatform();
|
|
auto range = constraint->getRequiredNewerAvailabilityRange(C);
|
|
|
|
// Only platform version constraints are supported currently.
|
|
ASSERT(platform != PlatformKind::none);
|
|
ASSERT(range);
|
|
|
|
versionCheck.emplace(platform, range->getRawMinimumVersion());
|
|
return true;
|
|
}
|
|
|
|
static std::pair<BraceStmt *, bool>
|
|
deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl, void *) {
|
|
// enum SomeEnum : SomeType {
|
|
// case A = 111, B = 222
|
|
// @available(iOS 10, *) case C = 333
|
|
// @derived
|
|
// init?(rawValue: SomeType) {
|
|
// switch rawValue {
|
|
// case 111:
|
|
// self = .A
|
|
// case 222:
|
|
// self = .B
|
|
// case 333:
|
|
// guard #available(iOS 10, *) else { return nil }
|
|
// self = .C
|
|
// default:
|
|
// return nil
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
auto parentDC = initDecl->getDeclContext();
|
|
ASTContext &C = parentDC->getASTContext();
|
|
auto availabilityContext = AvailabilityContext::forDeploymentTarget(C);
|
|
|
|
auto nominalTypeDecl = parentDC->getSelfNominalTypeDecl();
|
|
auto enumDecl = cast<EnumDecl>(nominalTypeDecl);
|
|
|
|
Type rawTy = enumDecl->getRawType();
|
|
assert(rawTy);
|
|
rawTy = initDecl->mapTypeIntoContext(rawTy);
|
|
|
|
bool isStringEnum = rawTy->isString();
|
|
llvm::SmallVector<Expr *, 16> stringExprs;
|
|
|
|
Type enumType = parentDC->getDeclaredTypeInContext();
|
|
|
|
auto selfDecl = cast<ConstructorDecl>(initDecl)->getImplicitSelfDecl();
|
|
|
|
SmallVector<ASTNode, 4> cases;
|
|
unsigned Idx = 0;
|
|
for (auto elt : enumDecl->getAllElements()) {
|
|
// First, check case availability. If the case will definitely be
|
|
// unavailable, skip it. If it might be unavailable at runtime, save
|
|
// information about that check in versionCheck and keep processing this
|
|
// element.
|
|
std::optional<RuntimeVersionCheck> versionCheck(std::nullopt);
|
|
if (!checkAvailability(elt, availabilityContext, versionCheck))
|
|
continue;
|
|
|
|
// litPat = elt.rawValueExpr as a pattern
|
|
LiteralExpr *litExpr = cloneRawLiteralExpr(C, elt->getRawValueExpr());
|
|
if (isStringEnum) {
|
|
// In case of a string enum we are calling the _findStringSwitchCase
|
|
// function from the library and switching on the returned Int value.
|
|
stringExprs.push_back(litExpr);
|
|
litExpr = IntegerLiteralExpr::createFromUnsigned(C, Idx, SourceLoc());
|
|
}
|
|
auto *litPat = ExprPattern::createImplicit(C, litExpr, /*DC*/ initDecl);
|
|
|
|
/// Statements in the body of this case.
|
|
SmallVector<ASTNode, 2> stmts;
|
|
|
|
// If checkAvailability() discovered we need a runtime version check,
|
|
// add it now.
|
|
if (versionCheck.has_value())
|
|
stmts.push_back(ASTNode(versionCheck->createEarlyReturnStmt(C)));
|
|
|
|
// Create a statement which assigns the case to self.
|
|
|
|
// valueExpr = "\(enumType).\(elt)"
|
|
auto metaTyRef = TypeExpr::createImplicit(enumType, C);
|
|
auto valueExpr = new (C) MemberRefExpr(metaTyRef, SourceLoc(),
|
|
elt, DeclNameLoc(), /*implicit*/true);
|
|
|
|
// assignment = "self = \(valueExpr)"
|
|
auto selfRef = new (C) DeclRefExpr(selfDecl, DeclNameLoc(),
|
|
/*implicit*/true,
|
|
AccessSemantics::DirectToStorage);
|
|
auto assignment = new (C) AssignExpr(selfRef, SourceLoc(), valueExpr,
|
|
/*implicit*/ true);
|
|
|
|
stmts.push_back(ASTNode(assignment));
|
|
|
|
// body = "{ \(stmts) }" (the braces are silent)
|
|
auto body = BraceStmt::create(C, SourceLoc(),
|
|
stmts, SourceLoc());
|
|
|
|
// cases.append("case \(litPat): \(body)")
|
|
cases.push_back(CaseStmt::create(C, CaseParentKind::Switch, SourceLoc(),
|
|
CaseLabelItem(litPat), SourceLoc(),
|
|
SourceLoc(), body,
|
|
/*case body var decls*/ std::nullopt));
|
|
++Idx;
|
|
}
|
|
|
|
auto anyPat = AnyPattern::createImplicit(C);
|
|
auto dfltLabelItem = CaseLabelItem::getDefault(anyPat);
|
|
|
|
auto dfltReturnStmt = new (C) FailStmt(SourceLoc(), SourceLoc());
|
|
auto dfltBody = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(dfltReturnStmt), SourceLoc());
|
|
cases.push_back(CaseStmt::create(C, CaseParentKind::Switch, SourceLoc(),
|
|
dfltLabelItem, SourceLoc(), SourceLoc(),
|
|
dfltBody,
|
|
/*case body var decls*/ std::nullopt));
|
|
|
|
auto rawDecl = initDecl->getParameters()->get(0);
|
|
auto rawRef = new (C) DeclRefExpr(rawDecl, DeclNameLoc(), /*implicit*/true);
|
|
Expr *switchArg = rawRef;
|
|
if (isStringEnum) {
|
|
// Call _findStringSwitchCase with an array of strings as argument.
|
|
auto *Fun = UnresolvedDeclRefExpr::createImplicit(
|
|
C, C.getIdentifier("_findStringSwitchCase"));
|
|
auto *strArray = ArrayExpr::create(C, SourceLoc(), stringExprs, {},
|
|
SourceLoc());
|
|
Argument args[] = {
|
|
Argument(SourceLoc(), C.getIdentifier("cases"), strArray),
|
|
Argument(SourceLoc(), C.getIdentifier("string"), rawRef)
|
|
};
|
|
auto *argList = ArgumentList::createImplicit(C, args);
|
|
switchArg = CallExpr::createImplicit(C, Fun, argList);
|
|
}
|
|
auto switchStmt =
|
|
SwitchStmt::createImplicit(LabeledStmtInfo(), switchArg, cases, C);
|
|
auto body = BraceStmt::create(C, SourceLoc(),
|
|
ASTNode(switchStmt),
|
|
SourceLoc());
|
|
return { body, /*isTypeChecked=*/false };
|
|
}
|
|
|
|
static ConstructorDecl *
|
|
deriveRawRepresentable_init(DerivedConformance &derived) {
|
|
ASTContext &C = derived.Context;
|
|
|
|
auto enumDecl = cast<EnumDecl>(derived.Nominal);
|
|
auto parentDC = derived.getConformanceContext();
|
|
auto rawInterfaceType = enumDecl->getRawType();
|
|
auto rawType = parentDC->mapTypeIntoContext(rawInterfaceType);
|
|
|
|
|
|
assert([&]() -> bool {
|
|
return TypeChecker::conformsToKnownProtocol(
|
|
rawType, KnownProtocolKind::Equatable);
|
|
}());
|
|
|
|
auto *rawDecl = new (C)
|
|
ParamDecl(SourceLoc(), SourceLoc(),
|
|
C.Id_rawValue, SourceLoc(), C.Id_rawValue, parentDC);
|
|
rawDecl->setSpecifier(ParamSpecifier::Default);
|
|
rawDecl->setInterfaceType(rawInterfaceType);
|
|
rawDecl->setImplicit();
|
|
auto paramList = ParameterList::createWithoutLoc(rawDecl);
|
|
|
|
DeclName name(C, DeclBaseName::createConstructor(), paramList);
|
|
|
|
auto initDecl =
|
|
new (C) ConstructorDecl(name, SourceLoc(),
|
|
/*Failable=*/true, /*FailabilityLoc=*/SourceLoc(),
|
|
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
|
|
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
|
/*ThrownType=*/TypeLoc(), paramList,
|
|
/*GenericParams=*/nullptr, parentDC,
|
|
/*LifetimeDependentTypeRepr*/ nullptr);
|
|
|
|
initDecl->setImplicit();
|
|
initDecl->setBodySynthesizer(&deriveBodyRawRepresentable_init);
|
|
addNonIsolatedToSynthesized(enumDecl, initDecl);
|
|
initDecl->copyFormalAccessFrom(enumDecl, /*sourceIsParentContext*/true);
|
|
|
|
// If the containing module is not resilient, make sure clients can construct
|
|
// an instance without function call overhead.
|
|
maybeMarkAsInlinable(derived, initDecl);
|
|
|
|
derived.addMembersToConformanceContext({initDecl});
|
|
return initDecl;
|
|
}
|
|
|
|
bool DerivedConformance::canDeriveRawRepresentable(DeclContext *DC,
|
|
NominalTypeDecl *type) {
|
|
auto enumDecl = dyn_cast<EnumDecl>(type);
|
|
if (!enumDecl)
|
|
return false;
|
|
|
|
Type rawType = enumDecl->getRawType();
|
|
if (!rawType || rawType->hasError())
|
|
return false;
|
|
|
|
if (!computeAutomaticEnumValueKind(enumDecl))
|
|
return false;
|
|
|
|
rawType = DC->mapTypeIntoContext(rawType);
|
|
|
|
auto inherited = enumDecl->getInherited().getEntries();
|
|
if (!inherited.empty() && inherited.front().wasValidated() &&
|
|
inherited.front().isError())
|
|
return false;
|
|
|
|
// The raw type must be Equatable, so that we have a suitable ~= for
|
|
// synthesized switch statements.
|
|
if (!TypeChecker::conformsToKnownProtocol(rawType, KnownProtocolKind::Equatable))
|
|
return false;
|
|
|
|
auto &C = type->getASTContext();
|
|
auto rawValueDecls = enumDecl->lookupDirect(DeclName(C.Id_RawValue));
|
|
if (rawValueDecls.size() > 1)
|
|
return false;
|
|
|
|
// Check that the RawValue matches the expected raw type.
|
|
if (!rawValueDecls.empty()) {
|
|
if (auto alias = dyn_cast<TypeDecl>(rawValueDecls.front())) {
|
|
auto ty = alias->getDeclaredInterfaceType();
|
|
if (!DC->mapTypeIntoContext(ty)->isEqual(rawType)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// There must be enum elements.
|
|
if (enumDecl->getAllElements().empty())
|
|
return false;
|
|
|
|
// Have the type-checker validate that:
|
|
// - the enum elements all have the same type
|
|
// - they all match the enum type
|
|
for (auto elt : enumDecl->getAllElements()) {
|
|
// We cannot synthesize raw representable conformance for an enum with
|
|
// cases that have a payload.
|
|
if (elt->hasAssociatedValues())
|
|
return false;
|
|
|
|
if (elt->isInvalid()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If it meets all of those requirements, we can synthesize RawRepresentable conformance.
|
|
return true;
|
|
}
|
|
|
|
ValueDecl *DerivedConformance::deriveRawRepresentable(ValueDecl *requirement) {
|
|
|
|
// Check preconditions for synthesized conformance.
|
|
if (!canDeriveRawRepresentable(cast<DeclContext>(ConformanceDecl), Nominal))
|
|
return nullptr;
|
|
|
|
if (requirement->getBaseName() == Context.Id_rawValue)
|
|
return deriveRawRepresentable_raw(*this);
|
|
|
|
if (requirement->getBaseName().isConstructor())
|
|
return deriveRawRepresentable_init(*this);
|
|
|
|
Context.Diags.diagnose(requirement->getLoc(),
|
|
diag::broken_raw_representable_requirement);
|
|
return nullptr;
|
|
}
|
|
|
|
Type DerivedConformance::deriveRawRepresentable(AssociatedTypeDecl *assocType) {
|
|
|
|
// Check preconditions for synthesized conformance.
|
|
if (!canDeriveRawRepresentable(cast<DeclContext>(ConformanceDecl), Nominal))
|
|
return nullptr;
|
|
|
|
if (assocType->getName() == Context.Id_RawValue) {
|
|
return deriveRawRepresentable_Raw(*this);
|
|
}
|
|
|
|
Context.Diags.diagnose(assocType->getLoc(),
|
|
diag::broken_raw_representable_requirement);
|
|
return nullptr;
|
|
}
|