Skip to content

Explicitly State CodingKeys as a Codable Requirement #28665

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

Merged
merged 6 commits into from
Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,12 @@ DECL_ATTR(derivative, Derivative,
ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove,
97)

DECL_ATTR(_implicitly_synthesizes_nested_requirement, ImplicitlySynthesizesNestedRequirement,
OnProtocol |
UserInaccessible |
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
98)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
17 changes: 17 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,23 @@ class SwiftNativeObjCRuntimeBaseAttr : public DeclAttribute {
}
};

/// Defines the @_implicitly_synthesizes_nested_requirement attribute.
class ImplicitlySynthesizesNestedRequirementAttr : public DeclAttribute {
public:
ImplicitlySynthesizesNestedRequirementAttr(StringRef Value, SourceLoc AtLoc,
SourceRange Range)
: DeclAttribute(DAK_ImplicitlySynthesizesNestedRequirement,
AtLoc, Range, /*Implicit*/false),
Value(Value) {}

/// The name of the phantom requirement.
const StringRef Value;

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_ImplicitlySynthesizesNestedRequirement;
}
};

/// Determine the result of comparing an availability attribute to a specific
/// platform or language version.
enum class AvailableVersionComparison {
Expand Down
19 changes: 18 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,11 +467,15 @@ class alignas(1 << DeclAlignInBits) Decl {
IsDebuggerAlias : 1
);

SWIFT_INLINE_BITFIELD(NominalTypeDecl, GenericTypeDecl, 1+1+1,
SWIFT_INLINE_BITFIELD(NominalTypeDecl, GenericTypeDecl, 1+1+1+1,
/// Whether we have already added implicitly-defined initializers
/// to this declaration.
AddedImplicitInitializers : 1,

/// Whether we have already added the phantom CodingKeys
/// nested requirement to this declaration.
AddedPhantomCodingKeys : 1,

/// Whether there is are lazily-loaded conformances for this nominal type.
HasLazyConformances : 1,

Expand Down Expand Up @@ -3323,6 +3327,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
IterableDeclContext(IterableDeclContextKind::NominalTypeDecl)
{
Bits.NominalTypeDecl.AddedImplicitInitializers = false;
Bits.NominalTypeDecl.AddedPhantomCodingKeys = false;
ExtensionGeneration = 0;
Bits.NominalTypeDecl.HasLazyConformances = false;
Bits.NominalTypeDecl.IsComputingSemanticMembers = false;
Expand Down Expand Up @@ -3363,6 +3368,18 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
Bits.NominalTypeDecl.AddedImplicitInitializers = true;
}

/// Determine whether we have already attempted to add the
/// phantom CodingKeys type to this declaration.
bool addedPhantomCodingKeys() const {
return Bits.NominalTypeDecl.AddedPhantomCodingKeys;
}

/// Note that we have attempted to add the phantom CodingKeys type
/// to this declaration.
void setAddedPhantomCodingKeys() {
Bits.NominalTypeDecl.AddedPhantomCodingKeys = true;
}

/// getDeclaredType - Retrieve the type declared by this entity, without
/// any generic parameters bound if this is a generic type.
Type getDeclaredType() const;
Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,8 @@ struct PrintOptions {
/// List of attribute kinds that should not be printed.
std::vector<AnyAttrKind> ExcludeAttrList = {DAK_Transparent, DAK_Effects,
DAK_FixedLayout,
DAK_ShowInInterface};
DAK_ShowInInterface,
DAK_ImplicitlySynthesizesNestedRequirement};

/// List of attribute kinds that should be printed exclusively.
/// Empty means allow all.
Expand Down
2 changes: 0 additions & 2 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -1679,8 +1679,6 @@ class InheritsSuperclassInitializersRequest
enum class ImplicitMemberAction : uint8_t {
ResolveImplicitInit,
ResolveCodingKeys,
ResolveEncodable,
ResolveDecodable,
};

class ResolveImplicitMemberRequest
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
break;
}

case DAK_ImplicitlySynthesizesNestedRequirement:
Printer.printAttrName("@_implicitly_synthesizes_nested_requirement");
Printer << "(\"" << cast<ImplicitlySynthesizesNestedRequirementAttr>(this)->Value << "\")";
break;

case DAK_Count:
llvm_unreachable("exceed declaration attribute kinds");

Expand Down Expand Up @@ -958,6 +963,8 @@ StringRef DeclAttribute::getAttrName() const {
return "_swift_native_objc_runtime_base";
case DAK_Semantics:
return "_semantics";
case DAK_ImplicitlySynthesizesNestedRequirement:
return "_implicitly_synthesizes_nested_requirement";
case DAK_Available:
return "availability";
case DAK_ObjC:
Expand Down
13 changes: 0 additions & 13 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3995,19 +3995,6 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) {
if (baseName.getIdentifier() == getASTContext().Id_CodingKeys) {
action.emplace(ImplicitMemberAction::ResolveCodingKeys);
}
} else {
auto argumentNames = member.getArgumentNames();
if (!member.isCompoundName() || argumentNames.size() == 1) {
if (baseName == DeclBaseName::createConstructor() &&
(member.isSimpleName() || argumentNames.front() == Context.Id_from)) {
action.emplace(ImplicitMemberAction::ResolveDecodable);
} else if (!baseName.isSpecial() &&
baseName.getIdentifier() == Context.Id_encode &&
(member.isSimpleName() ||
argumentNames.front() == Context.Id_to)) {
action.emplace(ImplicitMemberAction::ResolveEncodable);
}
}
}

if (auto actionToTake = action) {
Expand Down
6 changes: 0 additions & 6 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1074,12 +1074,6 @@ void swift::simple_display(llvm::raw_ostream &out,
case ImplicitMemberAction::ResolveCodingKeys:
out << "resolve CodingKeys";
break;
case ImplicitMemberAction::ResolveEncodable:
out << "resolve Encodable.encode(to:)";
break;
case ImplicitMemberAction::ResolveDecodable:
out << "resolve Decodable.init(from:)";
break;
}
}

Expand Down
37 changes: 37 additions & 0 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,43 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
}
break;
}

case DAK_ImplicitlySynthesizesNestedRequirement: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}

if (Tok.isNot(tok::string_literal)) {
diagnose(Loc, diag::attr_expected_string_literal, AttrName);
return false;
}

auto Value = getStringLiteralIfNotInterpolated(
Loc, ("'" + AttrName + "'").str());

consumeToken(tok::string_literal);

if (Value.hasValue())
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
else
DiscardAttribute = true;

if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}

if (!DiscardAttribute) {
Attributes.add(new (Context)
ImplicitlySynthesizesNestedRequirementAttr(Value.getValue(), AtLoc,
AttrRange));
}
break;
}

case DAK_Available: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
Expand Down
31 changes: 4 additions & 27 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1070,11 +1070,8 @@ ResolveImplicitMemberRequest::evaluate(Evaluator &evaluator,
// 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.
//
// Returns whether the target conforms to the protocol.
auto evaluateTargetConformanceTo = [&](ProtocolDecl *protocol) {
auto &Context = target->getASTContext();
auto tryToInstallCodingKeys = [&](ProtocolDecl *protocol) {
if (!protocol)
return false;

Expand All @@ -1097,7 +1094,6 @@ ResolveImplicitMemberRequest::evaluate(Evaluator &evaluator,
return true;
};

auto &Context = target->getASTContext();
switch (action) {
case ImplicitMemberAction::ResolveImplicitInit:
TypeChecker::addImplicitConstructors(target);
Expand All @@ -1115,30 +1111,11 @@ ResolveImplicitMemberRequest::evaluate(Evaluator &evaluator,
// synthesized, it will be synthesized.
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
if (!evaluateTargetConformanceTo(decodableProto)) {
(void)evaluateTargetConformanceTo(encodableProto);
if (!tryToInstallCodingKeys(decodableProto)) {
(void)tryToInstallCodingKeys(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;
}
Expand Down
Loading