Skip to content

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