Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🛑Compiler support for synthesizing Hashable._hash(into:) #15122

Closed

Conversation

lorentey
Copy link
Member

@lorentey lorentey commented Mar 9, 2018

This improves compiler support for synthesizing Hashable implementations, to make use of the resilient hashing interface introduced by #14913. It removes the default implementation of _hash(into:); the requirement is now automatically synthesized as needed.

Fully synthesized implementations of Hashable now generate a _hash(into:) implementation that does the actual work of hashing a type's components. They also add a hashValue implementation that simply instantiates a new hasher and feeds it to _hash(into:). For example, here is how Hashable conformance is derived for a simple struct:

struct Book: Hashable {
  let title: String
  let authors: [Author]
  let pageCount: Int

  @derived var hashValue: Int { 
    return _hashValue(for: self) // defined in stdlib
  }
  @derived func _hash(into hasher: inout _Hasher) {
    hasher.append(title)
    hasher.append(authors)
    hasher.append(pageCount)
  }
}

This also enables partial synthesis: when exactly one of hashValue or _hash(into:) was explicitly provided, the compiler automatically synthesizes the other requirement. This works in all cases where Hashable conformance can be implemented: in structs, enums, classes or extensions.

@lorentey
Copy link
Member Author

lorentey commented Mar 9, 2018

This is currently blocked by SR-7156; in particular, vtable problems cause issues in swift-corelibs-foundation.

@lorentey
Copy link
Member Author

lorentey commented Mar 9, 2018

@swift-ci smoke test

@lorentey lorentey changed the title Compiler support for synthesizing Hashable._hash(into:) 🛑Compiler support for synthesizing Hashable._hash(into:) Mar 9, 2018
@lorentey lorentey changed the title 🛑Compiler support for synthesizing Hashable._hash(into:) 🛑Compiler support for synthesizing Hashable._hash(into:) Mar 9, 2018
@lorentey
Copy link
Member Author

@swift-ci smoke test

@DougGregor
Copy link
Member

@swift-ci please smoke test Linux

lorentey added 5 commits April 4, 2018 18:43
This removes the default implementation of _hash(into:), and replaces it with automatic synthesis built into the compiler. Hashable can now be implemented by defining either hashValue or _hash(into:) -- the compiler supplies the missing half automatically, in all cases.

To determine which _hash(into:) implementation to generate, the synthesizer resolves hashValue -- if it finds a synthesized definition for it, the generated _hash(into:) body implements hashing from scratch, feeding components into the hasher. Otherwise, the body implements _hash(into:) in terms of hashValue.

# Conflicts:
#	lib/Sema/DerivedConformances.cpp
…into:)

Without this change, the Hashable synthesizer attempts to add _hash(into:) methods directly on CF types, which (somewhat unsurprisingly) fails with assertion below. (This situation is unique to CF types, which are imported with an implicit _CFObject conformance; we usually have an extension context or a non-imported type decl in which to put the derived methods.)

Assertion failed: (!decl->isForeign() && "Use getForeignMetadataLayout()"), function getClassMetadataLayout, file /Users/lorentey/Swift/swift/lib/IRGen/MetadataLayout.cpp, line 86.
0  swift                    0x0000000108e28b08 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40
1  swift                    0x0000000108e29216 SignalHandler(int) + 694
2  libsystem_platform.dylib 0x00007fff587aef5a _sigtramp + 26
3  libsystem_platform.dylib 0x0000000000003628 _sigtramp + 2810529512
4  libsystem_c.dylib        0x00007fff585571ae abort + 127
5  libsystem_c.dylib        0x00007fff5851f1ac basename_r + 0
6  swift                    0x0000000105a2a460 swift::irgen::IRGenModule::getClassMetadataLayout(swift::ClassDecl*) + 96
7  swift                    0x0000000105970dc8 swift::irgen::emitVirtualMethodValue(swift::irgen::IRGenFunction&, llvm::Value*, swift::SILDeclRef, swift::CanTypeWrapper<swift::SILFunctionType>) + 184
8  swift                    0x000000010597121d swift::irgen::emitVirtualMethodValue(swift::irgen::IRGenFunction&, llvm::Value*, swift::SILType, swift::SILDeclRef, swift::CanTypeWrapper<swift::SILFunctionType>, bool) + 685
9  swift                    0x0000000105a01482 swift::SILInstructionVisitor<(anonymous namespace)::IRGenSILFunction, void>::visit(swift::SILInstruction*) + 36562
10 swift                    0x00000001059f4e4f (anonymous namespace)::IRGenSILFunction::emitSILFunction() + 6863
11 swift                    0x00000001059f2e1b swift::irgen::IRGenModule::emitSILFunction(swift::SILFunction*) + 1371
12 swift                    0x00000001058fccab swift::irgen::IRGenerator::emitLazyDefinitions() + 1051
13 swift                    0x00000001059cf676 performIRGeneration(swift::IRGenOptions&, swift::ModuleDecl*, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule> >, llvm::StringRef, swift::PrimarySpecificPaths const&, llvm::LLVMContext&, swift::SourceFile*, llvm::GlobalVariable**, unsigned int) + 1366
14 swift                    0x00000001059cfc5e swift::performIRGeneration(swift::IRGenOptions&, swift::SourceFile&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule> >, llvm::StringRef, swift::PrimarySpecificPaths const&, llvm::LLVMContext&, unsigned int, llvm::GlobalVariable**) + 94
15 swift                    0x000000010586d2a9 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 15337
16 swift                    0x0000000105868715 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 2869
17 swift                    0x000000010581f300 main + 2832
18 libdyld.dylib            0x00007fff584ab015 start + 1
# Conflicts:
#	test/Sema/enum_conformance_synthesis.swift
_hash(into:) needs to be included in expectations; tests looking at synthesized Hashable implementation bodies need to be updated for resilient hashing.

# Conflicts:
#	test/IDE/complete_enum_elements.swift
@lorentey lorentey added the swift evolution pending discussion Flag → feature: A feature that has a Swift evolution proposal currently in review label Apr 4, 2018
@lorentey lorentey force-pushed the resilient-hashing.synthesis branch from fd770da to 05601da Compare April 4, 2018 18:13
Also rename Hasher.append() to Hasher.combine().

Hasher’s API is still incomplete: it currently only supports combining 32-bit and 64-bit values.
@slavapestov
Copy link
Contributor

@lorentey I didn't realize the Codable vtable bug was blocking this work. I'll take a look at it soon.

@lorentey lorentey added swift evolution approved Flag → feature: A feature that was approved through the Swift evolution process and removed swift evolution pending discussion Flag → feature: A feature that has a Swift evolution proposal currently in review labels Apr 20, 2018
@lorentey
Copy link
Member Author

This is now incorporated into and superseded by #16073.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
swift evolution approved Flag → feature: A feature that was approved through the Swift evolution process
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants