mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Rationalize Implicit Member Synthesis Somewhat
Codable's deep magic currently forces conformance checks in the middle of name lookup in order to inject CodingKeys into lookup results. This is compounded by the fact that this lookup fixup is occuring incrementally, meaning depending on order of requirements being looked up, Decl::getMembers() will give you a different answer. Compounding this, NameLookup relied on the LazyResolver to formalize this layering violation, and relied on implicit laziness to guard against re-entrancy. The approach is multi-pronged: 1) Shift the layering violation into the request evaluator 2) Spell out the kinds of resolution we support explicitly (make them easier to find and kill) 3) Remove the LazyResolver entrypoint this was relying on 4) Split off the property wrappers part into its own utility
This commit is contained in:
@@ -1075,12 +1075,12 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
|
||||
(void)decl->getDefaultInitializer();
|
||||
}
|
||||
|
||||
void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
|
||||
DeclName member) {
|
||||
auto baseName = member.getBaseName();
|
||||
|
||||
if (baseName == DeclBaseName::createConstructor())
|
||||
addImplicitConstructors(target);
|
||||
llvm::Expected<bool>
|
||||
ResolveImplicitMemberRequest::evaluate(Evaluator &evaluator,
|
||||
NominalTypeDecl *target,
|
||||
ImplicitMemberAction action) const {
|
||||
// FIXME: This entire request is a layering violation made of smaller,
|
||||
// finickier layering violations. See rdar://56844567
|
||||
|
||||
// Checks whether the target conforms to the given protocol. If the
|
||||
// conformance is incomplete, force the conformance.
|
||||
@@ -1091,9 +1091,9 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
|
||||
return false;
|
||||
|
||||
auto targetType = target->getDeclaredInterfaceType();
|
||||
auto ref =
|
||||
conformsToProtocol(targetType, protocol, target,
|
||||
ConformanceCheckFlags::SkipConditionalRequirements);
|
||||
auto ref = TypeChecker::conformsToProtocol(
|
||||
targetType, protocol, target,
|
||||
ConformanceCheckFlags::SkipConditionalRequirements);
|
||||
|
||||
if (ref.isInvalid()) {
|
||||
return false;
|
||||
@@ -1109,67 +1109,50 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
|
||||
return true;
|
||||
};
|
||||
|
||||
if (member.isSimpleName() && !baseName.isSpecial()) {
|
||||
if (baseName.getIdentifier() == Context.Id_CodingKeys) {
|
||||
// CodingKeys is a special type which may be synthesized as part of
|
||||
// Encodable/Decodable conformance. If the target conforms to either
|
||||
// protocol and would derive conformance to either, the type may be
|
||||
// synthesized.
|
||||
// If the target conforms to either and the conformance has not yet been
|
||||
// evaluated, then we should do that here.
|
||||
//
|
||||
// Try to synthesize Decodable first. If that fails, try to synthesize
|
||||
// Encodable. If either succeeds and CodingKeys should have been
|
||||
// synthesized, it will be synthesized.
|
||||
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
|
||||
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
|
||||
if (!evaluateTargetConformanceTo(decodableProto))
|
||||
(void)evaluateTargetConformanceTo(encodableProto);
|
||||
}
|
||||
|
||||
if ((baseName.getIdentifier().str().startswith("$") ||
|
||||
baseName.getIdentifier().str().startswith("_")) &&
|
||||
baseName.getIdentifier().str().size() > 1) {
|
||||
// $- and _-prefixed variables can be generated by properties that have
|
||||
// attached property wrappers.
|
||||
auto originalPropertyName =
|
||||
Context.getIdentifier(baseName.getIdentifier().str().substr(1));
|
||||
for (auto member : target->lookupDirect(originalPropertyName)) {
|
||||
if (auto var = dyn_cast<VarDecl>(member)) {
|
||||
if (var->hasAttachedPropertyWrapper()) {
|
||||
auto sourceFile = var->getDeclContext()->getParentSourceFile();
|
||||
if (sourceFile && sourceFile->Kind != SourceFileKind::Interface)
|
||||
(void)var->getPropertyWrapperBackingPropertyInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
auto argumentNames = member.getArgumentNames();
|
||||
if (member.isCompoundName() && argumentNames.size() != 1)
|
||||
return;
|
||||
|
||||
if (baseName == DeclBaseName::createConstructor() &&
|
||||
(member.isSimpleName() || argumentNames.front() == Context.Id_from)) {
|
||||
// init(from:) may be synthesized as part of derived conformance to the
|
||||
// Decodable protocol.
|
||||
// If the target should conform to the Decodable protocol, check the
|
||||
// conformance here to attempt synthesis.
|
||||
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
|
||||
(void)evaluateTargetConformanceTo(decodableProto);
|
||||
} else if (!baseName.isSpecial() &&
|
||||
baseName.getIdentifier() == Context.Id_encode &&
|
||||
(member.isSimpleName() ||
|
||||
argumentNames.front() == Context.Id_to)) {
|
||||
// encode(to:) may be synthesized as part of derived conformance to the
|
||||
// Encodable protocol.
|
||||
// If the target should conform to the Encodable protocol, check the
|
||||
// conformance here to attempt synthesis.
|
||||
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
|
||||
auto &Context = target->getASTContext();
|
||||
switch (action) {
|
||||
case ImplicitMemberAction::ResolveImplicitInit:
|
||||
TypeChecker::addImplicitConstructors(target);
|
||||
break;
|
||||
case ImplicitMemberAction::ResolveCodingKeys: {
|
||||
// CodingKeys is a special type which may be synthesized as part of
|
||||
// Encodable/Decodable conformance. If the target conforms to either
|
||||
// protocol and would derive conformance to either, the type may be
|
||||
// synthesized.
|
||||
// If the target conforms to either and the conformance has not yet been
|
||||
// evaluated, then we should do that here.
|
||||
//
|
||||
// Try to synthesize Decodable first. If that fails, try to synthesize
|
||||
// Encodable. If either succeeds and CodingKeys should have been
|
||||
// synthesized, it will be synthesized.
|
||||
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
|
||||
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
|
||||
if (!evaluateTargetConformanceTo(decodableProto)) {
|
||||
(void)evaluateTargetConformanceTo(encodableProto);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ImplicitMemberAction::ResolveEncodable: {
|
||||
// encode(to:) may be synthesized as part of derived conformance to the
|
||||
// Encodable protocol.
|
||||
// If the target should conform to the Encodable protocol, check the
|
||||
// conformance here to attempt synthesis.
|
||||
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
|
||||
(void)evaluateTargetConformanceTo(encodableProto);
|
||||
}
|
||||
break;
|
||||
case ImplicitMemberAction::ResolveDecodable: {
|
||||
// init(from:) may be synthesized as part of derived conformance to the
|
||||
// Decodable protocol.
|
||||
// If the target should conform to the Decodable protocol, check the
|
||||
// conformance here to attempt synthesis.
|
||||
TypeChecker::addImplicitConstructors(target);
|
||||
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
|
||||
(void)evaluateTargetConformanceTo(decodableProto);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::Expected<bool>
|
||||
|
||||
Reference in New Issue
Block a user