mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user