[Type checker] Synthesize member operator '==' for Equatable enums.

Rather than synthesizing a global operator '==' for Equatable enums,
synthesize a member operator, which is more idiomatic and much
cleaner.

To make sure that these synthesized operators can actually be found,
start considering operator requirements in protocols
more generally in the type checker, so that, e.g., "myEnum == myEnum"
will type-check against Equatable.== and, on successful type-check,
will call the (newly-synthesized) witness for '=='. This both makes it
easier to make sure we find the operators in, e.g., complex multi-file
and lazy-type checking scenarios, and is a step toward the
type-checking improvements described in SE-0091.
This commit is contained in:
Doug Gregor
2016-07-20 12:41:09 -07:00
parent 778e76e158
commit c56f96d237
14 changed files with 141 additions and 81 deletions

View File

@@ -171,23 +171,23 @@ static ValueDecl *
deriveEquatable_enum_eq(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) {
// enum SomeEnum<T...> {
// case A, B, C
// }
// @derived
// func ==<T...>(a: SomeEnum<T...>, b: SomeEnum<T...>) -> Bool {
// var index_a: Int
// switch a {
// case .A: index_a = 0
// case .B: index_a = 1
// case .C: index_a = 2
//
// @derived
// func ==(a: SomeEnum<T...>, b: SomeEnum<T...>) -> Bool {
// var index_a: Int
// switch a {
// case .A: index_a = 0
// case .B: index_a = 1
// case .C: index_a = 2
// }
// var index_b: Int
// switch b {
// case .A: index_b = 0
// case .B: index_b = 1
// case .C: index_b = 2
// }
// return index_a == index_b
// }
// var index_b: Int
// switch b {
// case .A: index_b = 0
// case .B: index_b = 1
// case .C: index_b = 2
// }
// return index_a == index_b
// }
ASTContext &C = tc.Context;
@@ -199,26 +199,30 @@ deriveEquatable_enum_eq(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) {
Identifier(), SourceLoc(), C.getIdentifier(s),
enumTy, parentDC);
};
auto params = ParameterList::create(C, {
getParamDecl("a"),
getParamDecl("b")
});
auto genericParams = parentDC->getGenericParamsOfContext();
auto boolTy = C.getBoolDecl()->getDeclaredType();
auto moduleDC = parentDecl->getModuleContext();
DeclName name(C, C.Id_EqualsOperator, params);
auto selfDecl = ParamDecl::createUnboundSelf(SourceLoc(), parentDC);
ParameterList *params[] = {
ParameterList::createWithoutLoc(selfDecl),
ParameterList::create(C, {
getParamDecl("a"),
getParamDecl("b")
})
};
auto boolTy = C.getBoolDecl()->getDeclaredType();
DeclName name(C, C.Id_EqualsOperator, params[1]);
auto eqDecl =
FuncDecl::create(C, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None,
FuncDecl::create(C, /*StaticLoc=*/SourceLoc(),
StaticSpellingKind::KeywordStatic,
/*FuncLoc=*/SourceLoc(), name, /*NameLoc=*/SourceLoc(),
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
/*AccessorKeywordLoc=*/SourceLoc(),
genericParams,
/*GenericParams=*/nullptr,
params, Type(),
TypeLoc::withoutLoc(boolTy),
&moduleDC->getDerivedFileUnit());
parentDC);
eqDecl->setImplicit();
eqDecl->getAttrs().add(new (C) InfixAttr(/*implicit*/false));
auto op = C.getStdlibModule()->lookupInfixOperator(C.Id_EqualsOperator);
@@ -232,35 +236,41 @@ deriveEquatable_enum_eq(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) {
return nullptr;
}
// Fill in the 'self' type.
Type selfTy = eqDecl->computeSelfType();
selfDecl->overwriteType(selfTy);
eqDecl->setOperatorDecl(op);
eqDecl->setDerivedForTypeDecl(enumDecl);
eqDecl->setBodySynthesizer(&deriveBodyEquatable_enum_eq);
// Compute the type.
auto paramsTy = params->getType(C);
Type fnTy;
GenericParamList *genericParams = eqDecl->getGenericParamsOfContext();
Type paramsTy = params[1]->getType(tc.Context);
Type fnTy = FunctionType::get(paramsTy, boolTy);
if (genericParams)
fnTy = PolymorphicFunctionType::get(paramsTy, boolTy, genericParams);
fnTy = PolymorphicFunctionType::get(selfTy, fnTy, genericParams);
else
fnTy = FunctionType::get(paramsTy, boolTy);
fnTy = FunctionType::get(selfTy, fnTy);
eqDecl->setType(fnTy);
// Compute the interface type.
Type interfaceTy;
Type selfIfaceTy = eqDecl->computeInterfaceSelfType(false);
if (auto genericSig = parentDC->getGenericSignatureOfContext()) {
eqDecl->setGenericSignature(genericSig);
auto enumIfaceTy = parentDC->getDeclaredInterfaceType();
Type enumIfaceTy = parentDC->getDeclaredInterfaceType();
TupleTypeElt ifaceParamElts[] = {
enumIfaceTy, enumIfaceTy,
};
auto ifaceParamsTy = TupleType::get(ifaceParamElts, C);
interfaceTy = GenericFunctionType::get(
genericSig, ifaceParamsTy, boolTy,
AnyFunctionType::ExtInfo());
interfaceTy = FunctionType::get(ifaceParamsTy, boolTy,
AnyFunctionType::ExtInfo());
interfaceTy = GenericFunctionType::get(genericSig, selfIfaceTy, interfaceTy,
AnyFunctionType::ExtInfo());
} else {
interfaceTy = FunctionType::get(paramsTy, boolTy);
interfaceTy = FunctionType::get(selfIfaceTy, interfaceTy);
}
eqDecl->setInterfaceType(interfaceTy);
@@ -275,8 +285,8 @@ deriveEquatable_enum_eq(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) {
if (enumDecl->hasClangNode())
tc.Context.addExternalDecl(eqDecl);
// Since it's an operator we insert the decl after the type at global scope.
insertOperatorDecl(C, cast<IterableDeclContext>(parentDecl), eqDecl);
// Add the operator to the parent scope.
cast<IterableDeclContext>(parentDecl)->addMember(eqDecl);
return eqDecl;
}