/// Derive a 'hashValue' implementation. static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { // @derived var hashValue: Int { // return _hashValue(for: self) // } auto &tc = derived.TC; ASTContext &C = tc.Context; auto parentDC = derived.getConformanceContext(); Type intType = C.getIntDecl()->getDeclaredType(); // We can't form a Hashable conformance if Int isn't Hashable or // ExpressibleByIntegerLiteral. if (!tc.conformsToProtocol(intType, C.getProtocol(KnownProtocolKind::Hashable), parentDC, None)) { tc.diagnose(derived.ConformanceDecl, diag::broken_int_hashable_conformance); return nullptr; } ProtocolDecl *intLiteralProto = C.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral); if (!tc.conformsToProtocol(intType, intLiteralProto, parentDC, None)) { tc.diagnose(derived.ConformanceDecl, diag::broken_int_integer_literal_convertible_conformance); return nullptr; } VarDecl *hashValueDecl = new (C) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var, /*IsCaptureList*/false, SourceLoc(), C.Id_hashValue, intType, parentDC); auto *selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC); ParameterList *params = ParameterList::createEmpty(C); AccessorDecl *getterDecl = AccessorDecl::create(C, /*FuncLoc=*/SourceLoc(), /*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, AddressorKind::NotAddressor, hashValueDecl, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*GenericParams=*/nullptr, selfDecl, params, TypeLoc::withoutLoc(intType), parentDC); getterDecl->setImplicit(); getterDecl->setBodySynthesizer(&deriveBodyHashable_hashValue); // Compute the interface type of hashValue(). if (auto env = parentDC->getGenericEnvironmentOfContext()) getterDecl->setGenericEnvironment(env); getterDecl->computeType(); getterDecl->setValidationToChecked(); getterDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); // Finish creating the property. hashValueDecl->setImplicit(); hashValueDecl->setInterfaceType(intType); hashValueDecl->setValidationToChecked(); hashValueDecl->setAccessors(StorageImplInfo::getImmutableComputed(), SourceLoc(), {getterDecl}, SourceLoc()); hashValueDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); Pattern *hashValuePat = new (C) NamedPattern(hashValueDecl, /*implicit*/true); hashValuePat->setType(intType); hashValuePat = new (C) TypedPattern(hashValuePat, TypeLoc::withoutLoc(intType), /*implicit*/ true); hashValuePat->setType(intType); auto *patDecl = PatternBindingDecl::createImplicit( C, StaticSpellingKind::None, hashValuePat, /*InitExpr*/ nullptr, parentDC); C.addSynthesizedDecl(hashValueDecl); C.addSynthesizedDecl(getterDecl); derived.addMembersToConformanceContext({getterDecl, hashValueDecl, patDecl}); return hashValueDecl; }
/// Derive a 'hashValue' implementation for an enum. static ValueDecl * deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, void (*bodySynthesizer)(AbstractFunctionDecl *)) { // enum SomeEnum { // case A, B, C // @derived var hashValue: Int { // var result: Int // switch self { // case A: // result = 0 // case B: // result = 1 // case C: // result = 2 // } // return result // } // } // // enum SomeEnumWithAssociatedValues { // case A, B(Int), C(String, Int) // @derived var hashValue: Int { // var result: Int // switch self { // case A: // result = 0 // case B(let a0): // result = 1 // result = _combineHashValues(result, a0.hashValue) // case C(let a0, let a1): // result = 2 // result = _combineHashValues(result, a0.hashValue) // result = _combineHashValues(result, a1.hashValue) // } // return result // } // } // // struct SomeStruct { // var x: Int // var y: String // @derived var hashValue: Int { // var result = 0 // result = _combineHashValues(result, x.hashValue) // result = _combineHashValues(result, y.hashValue) // return result // } // } ASTContext &C = tc.Context; auto parentDC = cast<DeclContext>(parentDecl); Type intType = C.getIntDecl()->getDeclaredType(); // We can't form a Hashable conformance if Int isn't Hashable or // ExpressibleByIntegerLiteral. if (!tc.conformsToProtocol(intType,C.getProtocol(KnownProtocolKind::Hashable), typeDecl, None)) { tc.diagnose(typeDecl->getLoc(), diag::broken_int_hashable_conformance); return nullptr; } ProtocolDecl *intLiteralProto = C.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral); if (!tc.conformsToProtocol(intType, intLiteralProto, typeDecl, None)) { tc.diagnose(typeDecl->getLoc(), diag::broken_int_integer_literal_convertible_conformance); return nullptr; } VarDecl *hashValueDecl = new (C) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var, /*IsCaptureList*/false, SourceLoc(), C.Id_hashValue, intType, parentDC); auto selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC); ParameterList *params[] = { ParameterList::createWithoutLoc(selfDecl), ParameterList::createEmpty(C) }; AccessorDecl *getterDecl = AccessorDecl::create(C, /*FuncLoc=*/SourceLoc(), /*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::IsGetter, AddressorKind::NotAddressor, hashValueDecl, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*GenericParams=*/nullptr, params, TypeLoc::withoutLoc(intType), parentDC); getterDecl->setImplicit(); getterDecl->setBodySynthesizer(bodySynthesizer); // Compute the type of hashValue(). Type methodType = FunctionType::get(TupleType::getEmpty(tc.Context), intType); // Compute the interface type of hashValue(). Type interfaceType; auto selfParam = computeSelfParam(getterDecl); if (auto sig = parentDC->getGenericSignatureOfContext()) { getterDecl->setGenericEnvironment(parentDC->getGenericEnvironmentOfContext()); interfaceType = GenericFunctionType::get(sig, {selfParam}, methodType, AnyFunctionType::ExtInfo()); } else interfaceType = FunctionType::get({selfParam}, methodType, AnyFunctionType::ExtInfo()); getterDecl->setInterfaceType(interfaceType); getterDecl->setValidationStarted(); getterDecl->copyFormalAccessFrom(typeDecl, /*sourceIsParentContext*/true); // Finish creating the property. hashValueDecl->setImplicit(); hashValueDecl->setInterfaceType(intType); hashValueDecl->setValidationStarted(); hashValueDecl->makeComputed(SourceLoc(), getterDecl, nullptr, nullptr, SourceLoc()); hashValueDecl->copyFormalAccessFrom(typeDecl, /*sourceIsParentContext*/true); Pattern *hashValuePat = new (C) NamedPattern(hashValueDecl, /*implicit*/true); hashValuePat->setType(intType); hashValuePat = new (C) TypedPattern(hashValuePat, TypeLoc::withoutLoc(intType), /*implicit*/ true); hashValuePat->setType(intType); auto patDecl = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), hashValuePat, nullptr, parentDC); patDecl->setImplicit(); tc.Context.addSynthesizedDecl(hashValueDecl); tc.Context.addSynthesizedDecl(getterDecl); auto dc = cast<IterableDeclContext>(parentDecl); dc->addMember(getterDecl); dc->addMember(hashValueDecl); dc->addMember(patDecl); return hashValueDecl; }