Files
swift-mirror/lib/Sema/DerivedConformanceRawRepresentable.cpp
Chris Lattner 7de9c0802a Peel off almost all of the uses of MetaTypeExpr, replacing
them with uses of TypeExpr instead.  The remaining uses of 
MetaTypeExpr (which will be renamed soon) are places where we
are applying the ".dynamicType" virtual property to an expression.

Unadorned uses of types in code, e.g. the Int in "Int.self" are
now represented with TypeExpr.

One unfortunate travesty that doing this work revealed is that we
are extremely sloppy and terrible about maintaining location information
in implicitly generated decls, and our invariants vary quite a bit.  This
is really horrible, but I'm not sure whether I'll go fix the hacks or not.

This patch perpetuates the existing crimes, but makes them more visible.

NFC!




Swift SVN r16646
2014-04-22 05:15:44 +00:00

436 lines
17 KiB
C++

//===--- DerivedConformanceRawRepresentable.cpp - Derived RawRepresentable ===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 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 "llvm/Support/raw_ostream.h"
#include "swift/AST/ArchetypeBuilder.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Types.h"
#include "DerivedConformances.h"
using namespace swift;
using namespace DerivedConformance;
void DerivedConformance::_insertOperatorDecl(NominalTypeDecl *scope,
Decl *member) {
// Find the module.
auto &C = scope->getASTContext();
auto mod = scope->getModuleContext();
// Add it to the module in a DerivedFileUnit.
mod->getDerivedFileUnit().addDerivedDecl(cast<FuncDecl>(member));
// Add it as a derived global decl to the nominal type.
auto oldDerived = scope->getDerivedGlobalDecls();
auto oldSize = std::distance(oldDerived.begin(), oldDerived.end());
auto newDerived = C.Allocate<Decl*>(oldSize + 1);
std::move(oldDerived.begin(), oldDerived.end(), newDerived.begin());
newDerived[oldSize] = member;
scope->setDerivedGlobalDecls(newDerived);
}
static LiteralExpr *cloneRawLiteralExpr(ASTContext &C, LiteralExpr *expr) {
LiteralExpr *clone;
if (auto intLit = dyn_cast<IntegerLiteralExpr>(expr)) {
clone = new (C) IntegerLiteralExpr(intLit->getDigitsText(), SourceLoc(),
/*implicit*/ true);
if (intLit->isNegative())
cast<IntegerLiteralExpr>(clone)->setNegative(SourceLoc());
} else if (auto charLit = dyn_cast<CharacterLiteralExpr>(expr)) {
clone = new (C) CharacterLiteralExpr(charLit->getValue(), SourceLoc());
} else if (auto stringLit = dyn_cast<StringLiteralExpr>(expr)) {
clone = new (C) StringLiteralExpr(stringLit->getValue(), SourceLoc());
} else if (auto floatLit = dyn_cast<FloatLiteralExpr>(expr)) {
clone = new (C) FloatLiteralExpr(floatLit->getText(), SourceLoc(),
/*implicit*/ true);
} else {
llvm_unreachable("invalid raw literal expr");
}
clone->setImplicit();
return clone;
}
static TypeDecl *deriveRawRepresentable_RawType(TypeChecker &tc,
EnumDecl *enumDecl) {
// enum SomeEnum : SomeType {
// typealias [derived] RawType = SomeType
// }
ASTContext &C = tc.Context;
auto rawInterfaceType = enumDecl->getRawType();
auto rawType = ArchetypeBuilder::mapTypeIntoContext(enumDecl,
rawInterfaceType);
auto rawTypeDecl = new (C) TypeAliasDecl(SourceLoc(),
C.Id_RawType,
SourceLoc(),
TypeLoc::withoutLoc(rawType),
enumDecl);
rawTypeDecl->setImplicit();
rawTypeDecl->setType(rawType);
rawTypeDecl->setInterfaceType(rawInterfaceType);
enumDecl->addMember(rawTypeDecl);
return rawTypeDecl;
}
static void deriveBodyRawRepresentable_toRaw(AbstractFunctionDecl *toRawDecl) {
auto enumDecl = cast<EnumDecl>(toRawDecl->getDeclContext());
Type rawTy = enumDecl->getRawType();
assert(rawTy);
for (auto elt : enumDecl->getAllElements()) {
if (!elt->getTypeCheckedRawValueExpr() ||
!elt->getTypeCheckedRawValueExpr()->getType()->isEqual(rawTy)) {
return;
}
}
ASTContext &C = enumDecl->getASTContext();
Type enumType = enumDecl->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));
}
Pattern *curriedArgs = toRawDecl->getBodyParamPatterns().front();
auto selfPattern =
cast<NamedPattern>(curriedArgs->getSemanticsProvidingPattern());
auto selfDecl = selfPattern->getDecl();
auto selfRef = new (C) DeclRefExpr(selfDecl, SourceLoc(), /*implicit*/true);
auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), selfRef,
SourceLoc(), cases, SourceLoc(), C);
auto body = BraceStmt::create(C, SourceLoc(),
ASTNode(switchStmt),
SourceLoc());
toRawDecl->setBody(body);
}
static FuncDecl *deriveRawRepresentable_toRaw(TypeChecker &tc,
EnumDecl *enumDecl) {
// enum SomeEnum : SomeType {
// case A = 111, B = 222
// func [derived] toRaw() -> SomeType {
// switch self {
// case A:
// return 111
// case B:
// return 222
// }
// }
// }
ASTContext &C = tc.Context;
auto rawInterfaceType = enumDecl->getRawType();
auto rawType = ArchetypeBuilder::mapTypeIntoContext(enumDecl,
rawInterfaceType);
Type enumType = enumDecl->getDeclaredTypeInContext();
VarDecl *selfDecl = new (C) ParamDecl(/*IsLet*/true,
SourceLoc(),
Identifier(),
SourceLoc(),
C.Id_self,
enumType,
enumDecl);
selfDecl->setImplicit();
Pattern *selfParam = new (C) NamedPattern(selfDecl, /*implicit*/ true);
selfParam->setType(enumType);
selfParam = new (C) TypedPattern(selfParam, TypeLoc::withoutLoc(enumType));
selfParam->setType(enumType);
Pattern *methodParam = TuplePattern::create(C, SourceLoc(),{},SourceLoc());
methodParam->setType(TupleType::getEmpty(tc.Context));
Pattern *params[] = {selfParam, methodParam};
DeclName name(C, C.Id_toRaw, { });
FuncDecl *toRawDecl =
FuncDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(),
name, SourceLoc(), nullptr, Type(),
params, TypeLoc::withoutLoc(rawType), enumDecl);
toRawDecl->setImplicit();
toRawDecl->setBodySynthesizer(&deriveBodyRawRepresentable_toRaw);
// Compute the type of toRaw().
GenericParamList *genericParams = nullptr;
Type type = FunctionType::get(TupleType::getEmpty(tc.Context), rawType);
Type selfType = toRawDecl->computeSelfType(&genericParams);
if (genericParams)
type = PolymorphicFunctionType::get(selfType, type, genericParams);
else
type = FunctionType::get(selfType, type);
toRawDecl->setType(type);
toRawDecl->setBodyResultType(rawType);
// Compute the interface type of toRaw();
Type interfaceType = FunctionType::get(TupleType::getEmpty(tc.Context),
rawInterfaceType);
Type selfInterfaceType = toRawDecl->computeInterfaceSelfType(false);
if (auto sig = enumDecl->getGenericSignatureOfContext())
interfaceType = GenericFunctionType::get(sig, selfInterfaceType,
interfaceType,
FunctionType::ExtInfo());
else
interfaceType = type;
toRawDecl->setInterfaceType(interfaceType);
if (enumDecl->hasClangNode())
tc.implicitlyDefinedFunctions.push_back(toRawDecl);
enumDecl->addMember(toRawDecl);
return toRawDecl;
}
static void
deriveBodyRawRepresentable_frowRaw(AbstractFunctionDecl *fromRawDecl) {
auto enumDecl = cast<EnumDecl>(fromRawDecl->getDeclContext());
Type rawTy = enumDecl->getRawType();
assert(rawTy);
for (auto elt : enumDecl->getAllElements()) {
if (!elt->getTypeCheckedRawValueExpr() ||
!elt->getTypeCheckedRawValueExpr()->getType()->isEqual(rawTy)) {
return;
}
}
ASTContext &C = enumDecl->getASTContext();
Type enumType = enumDecl->getDeclaredTypeInContext();
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, SourceLoc(), /*implicit*/true);
auto metaTyRef = TypeExpr::createImplicit(enumType, C);
auto returnExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef);
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 anyPat = new (C) AnyPattern(SourceLoc());
anyPat->setImplicit();
auto dfltLabelItem =
CaseLabelItem(/*IsDefault=*/true, anyPat, SourceLoc(), nullptr);
auto optionalRef = new (C) DeclRefExpr(C.getOptionalDecl(),
SourceLoc(), /*implicit*/true);
auto emptyArgs = new (C) TupleExpr(SourceLoc(), SourceLoc(),
/*implicit*/ true);
auto dfltReturnExpr = new (C) CallExpr(optionalRef, emptyArgs,
/*implicit*/ true);
auto dfltReturnStmt = new (C) ReturnStmt(SourceLoc(), dfltReturnExpr);
auto dfltBody = BraceStmt::create(C, SourceLoc(),
ASTNode(dfltReturnStmt), SourceLoc());
cases.push_back(CaseStmt::create(C, SourceLoc(), dfltLabelItem,
/*HasBoundDecls=*/false, SourceLoc(),
dfltBody));
Pattern *args = fromRawDecl->getBodyParamPatterns().back();
auto rawArgPattern = cast<NamedPattern>(args->getSemanticsProvidingPattern());
auto rawDecl = rawArgPattern->getDecl();
auto rawRef = new (C) DeclRefExpr(rawDecl, SourceLoc(), /*implicit*/true);
auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), rawRef,
SourceLoc(), cases, SourceLoc(), C);
auto body = BraceStmt::create(C, SourceLoc(),
ASTNode(switchStmt),
SourceLoc());
fromRawDecl->setBody(body);
}
static FuncDecl *deriveRawRepresentable_fromRaw(TypeChecker &tc,
EnumDecl *enumDecl) {
// enum SomeEnum : SomeType {
// case A = 111, B = 222
// @derived
// static func fromRaw(raw: SomeType) -> SomeEnum? {
// switch raw {
// case 111:
// return A
// case 222:
// return B
// default:
// return Optional()
// }
// }
// }
ASTContext &C = tc.Context;
auto rawInterfaceType = enumDecl->getRawType();
auto rawType = ArchetypeBuilder::mapTypeIntoContext(enumDecl,
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)) {
SourceLoc loc = enumDecl->getInherited()[0].getSourceRange().Start;
tc.diagnose(loc, diag::enum_raw_type_not_equatable, rawType);
return nullptr;
}
Type enumType = enumDecl->getDeclaredTypeInContext();
Type enumMetaType = MetatypeType::get(enumType);
VarDecl *selfDecl = new (C) ParamDecl(/*IsVal*/true,
SourceLoc(),
Identifier(),
SourceLoc(),
C.Id_self,
enumMetaType,
enumDecl);
selfDecl->setImplicit();
Pattern *selfParam = new (C) NamedPattern(selfDecl, /*implicit*/ true);
selfParam->setType(enumMetaType);
selfParam = new (C) TypedPattern(selfParam,
TypeLoc::withoutLoc(enumMetaType));
selfParam->setType(enumMetaType);
selfParam->setImplicit();
VarDecl *rawDecl = new (C) ParamDecl(/*IsVal*/true,
SourceLoc(),
C.Id_raw,
SourceLoc(),
C.Id_raw,
rawType,
enumDecl);
rawDecl->setImplicit();
Pattern *rawParam = new (C) NamedPattern(rawDecl, /*implicit*/ true);
rawParam->setType(rawType);
rawParam = new (C) TypedPattern(rawParam, TypeLoc::withoutLoc(rawType));
rawParam->setType(rawType);
rawParam->setImplicit();
rawParam = new (C) ParenPattern(SourceLoc(), rawParam, SourceLoc());
rawParam->setType(rawType);
rawParam->setImplicit();
Pattern *bodyParams[] = {selfParam, rawParam};
auto retTy = OptionalType::get(enumType);
DeclName name(C, C.Id_fromRaw, { C.Id_raw });
auto fromRawDecl = FuncDecl::create(
C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), name, SourceLoc(),
nullptr, Type(), bodyParams, TypeLoc::withoutLoc(retTy), enumDecl);
fromRawDecl->setStatic();
fromRawDecl->setImplicit();
fromRawDecl->setBodySynthesizer(&deriveBodyRawRepresentable_frowRaw);
// Compute the type of fromRaw().
GenericParamList *genericParams = nullptr;
Type type = FunctionType::get(rawType, retTy);
Type selfType = fromRawDecl->computeSelfType(&genericParams);
if (genericParams)
type = PolymorphicFunctionType::get(selfType, type, genericParams);
else
type = FunctionType::get(selfType, type);
fromRawDecl->setType(type);
fromRawDecl->setBodyResultType(retTy);
// Compute the interface type of fromRaw();
Type retInterfaceType
= OptionalType::get(enumDecl->getDeclaredInterfaceType());
Type interfaceType = FunctionType::get(rawInterfaceType, retInterfaceType);
Type selfInterfaceType = fromRawDecl->computeInterfaceSelfType(false);
if (auto sig = enumDecl->getGenericSignatureOfContext())
interfaceType = GenericFunctionType::get(sig, selfInterfaceType,
interfaceType,
FunctionType::ExtInfo());
else
interfaceType = type;
fromRawDecl->setInterfaceType(interfaceType);
if (enumDecl->hasClangNode())
tc.implicitlyDefinedFunctions.push_back(fromRawDecl);
enumDecl->addMember(fromRawDecl);
return fromRawDecl;
}
ValueDecl *DerivedConformance::deriveRawRepresentable(TypeChecker &tc,
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);
// Start building the conforming decls.
if (requirement->getName() == tc.Context.Id_toRaw)
return deriveRawRepresentable_toRaw(tc, enumDecl);
if (requirement->getName() == tc.Context.Id_fromRaw)
return deriveRawRepresentable_fromRaw(tc, enumDecl);
if (requirement->getName() == tc.Context.Id_RawType)
return deriveRawRepresentable_RawType(tc, enumDecl);
tc.diagnose(requirement->getLoc(),
diag::broken_raw_representable_requirement);
return nullptr;
}