From c41aabee66f475958062129e8590b34061992aac Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Wed, 15 Mar 2017 14:53:55 -0700 Subject: [PATCH 1/9] Explicitly derive Codable & CodingKey conformance Integrate synthesis of Codable and CodingKey requirements along with preliminary unit tests --- include/swift/AST/Decl.h | 11 +- include/swift/AST/DiagnosticsSema.def | 4 + include/swift/AST/KnownIdentifiers.def | 21 + include/swift/AST/KnownProtocols.def | 2 + lib/AST/ASTContext.cpp | 4 +- lib/AST/Decl.cpp | 30 + lib/IRGen/GenMeta.cpp | 2 + lib/Sema/CMakeLists.txt | 2 + lib/Sema/DerivedConformanceCodable.cpp | 949 ++++++++++++++++++ lib/Sema/DerivedConformanceCodingKey.cpp | 615 ++++++++++++ lib/Sema/DerivedConformances.cpp | 37 +- lib/Sema/DerivedConformances.h | 16 + lib/Sema/TypeCheckProtocol.cpp | 6 + lib/Sema/TypeCheckType.cpp | 3 + lib/Sema/TypeChecker.h | 2 + .../Inputs/class_codable_simple_multi1.swift | 25 + .../Inputs/class_codable_simple_multi2.swift | 10 + .../enum_coding_key_extension_multi1.swift | 11 + .../enum_coding_key_extension_multi2.swift | 7 + .../enum_coding_key_extension_multi3.swift | 16 + .../Inputs/enum_coding_key_multi1.swift | 15 + .../Inputs/enum_coding_key_multi2.swift | 16 + .../Inputs/struct_codable_simple_multi1.swift | 27 + .../Inputs/struct_codable_simple_multi2.swift | 10 + .../coding/class_codable_computed_vars.swift | 43 + .../coding/class_codable_inheritance.swift | 59 ++ .../special/coding/class_codable_simple.swift | 38 + .../class_codable_simple_extension.swift | 40 + .../coding/class_codable_simple_multi.swift | 2 + .../special/coding/enum_coding_key.swift | 103 ++ .../coding/enum_coding_key_empty.swift | 13 + .../coding/enum_coding_key_extension.swift | 40 + .../enum_coding_key_extension_multi.swift | 6 + .../coding/enum_coding_key_int_raw_type.swift | 52 + .../coding/enum_coding_key_multi.swift | 2 + .../coding/enum_coding_key_no_raw_type.swift | 51 + ...um_coding_key_partial_implementation.swift | 151 +++ .../enum_coding_key_string_raw_type.swift | 55 + .../coding/struct_codable_computed_vars.swift | 40 + .../coding/struct_codable_simple.swift | 35 + .../struct_codable_simple_extension.swift | 40 + .../coding/struct_codable_simple_multi.swift | 2 + 42 files changed, 2606 insertions(+), 7 deletions(-) create mode 100644 lib/Sema/DerivedConformanceCodable.cpp create mode 100644 lib/Sema/DerivedConformanceCodingKey.cpp create mode 100644 test/decl/protocol/special/coding/Inputs/class_codable_simple_multi1.swift create mode 100644 test/decl/protocol/special/coding/Inputs/class_codable_simple_multi2.swift create mode 100644 test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi1.swift create mode 100644 test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi2.swift create mode 100644 test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi3.swift create mode 100644 test/decl/protocol/special/coding/Inputs/enum_coding_key_multi1.swift create mode 100644 test/decl/protocol/special/coding/Inputs/enum_coding_key_multi2.swift create mode 100644 test/decl/protocol/special/coding/Inputs/struct_codable_simple_multi1.swift create mode 100644 test/decl/protocol/special/coding/Inputs/struct_codable_simple_multi2.swift create mode 100644 test/decl/protocol/special/coding/class_codable_computed_vars.swift create mode 100644 test/decl/protocol/special/coding/class_codable_inheritance.swift create mode 100644 test/decl/protocol/special/coding/class_codable_simple.swift create mode 100644 test/decl/protocol/special/coding/class_codable_simple_extension.swift create mode 100644 test/decl/protocol/special/coding/class_codable_simple_multi.swift create mode 100644 test/decl/protocol/special/coding/enum_coding_key.swift create mode 100644 test/decl/protocol/special/coding/enum_coding_key_empty.swift create mode 100644 test/decl/protocol/special/coding/enum_coding_key_extension.swift create mode 100644 test/decl/protocol/special/coding/enum_coding_key_extension_multi.swift create mode 100644 test/decl/protocol/special/coding/enum_coding_key_int_raw_type.swift create mode 100644 test/decl/protocol/special/coding/enum_coding_key_multi.swift create mode 100644 test/decl/protocol/special/coding/enum_coding_key_no_raw_type.swift create mode 100644 test/decl/protocol/special/coding/enum_coding_key_partial_implementation.swift create mode 100644 test/decl/protocol/special/coding/enum_coding_key_string_raw_type.swift create mode 100644 test/decl/protocol/special/coding/struct_codable_computed_vars.swift create mode 100644 test/decl/protocol/special/coding/struct_codable_simple.swift create mode 100644 test/decl/protocol/special/coding/struct_codable_simple_extension.swift create mode 100644 test/decl/protocol/special/coding/struct_codable_simple_multi.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index fc285c4df5cde..c3d4fb3a0293b 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2883,6 +2883,9 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { private: /// Predicate used to filter StoredPropertyRange. struct ToStoredProperty { + ToStoredProperty(bool skipInaccessible = false) : + skipUserInaccessible(skipInaccessible) {} + bool skipUserInaccessible; Optional operator()(Decl *decl) const; }; @@ -2892,8 +2895,9 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { ToStoredProperty>; /// Return a collection of the stored member variables of this type. - StoredPropertyRange getStoredProperties() const { - return StoredPropertyRange(getMembers(), ToStoredProperty()); + StoredPropertyRange getStoredProperties(bool skipInaccessible = false) const { + return StoredPropertyRange(getMembers(), + ToStoredProperty(skipInaccessible)); } // Implement isa/cast/dyncast/etc. @@ -5985,7 +5989,8 @@ inline bool ValueDecl::isSettable(const DeclContext *UseDC, inline Optional NominalTypeDecl::ToStoredProperty::operator()(Decl *decl) const { if (auto var = dyn_cast(decl)) { - if (!var->isStatic() && var->hasStorage()) + if (!var->isStatic() && var->hasStorage() && + (!skipUserInaccessible || var->isUserAccessible())) return var; } diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index f2df11991868c..2a8d767f677e0 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1895,6 +1895,10 @@ ERROR(broken_equatable_eq_operator,none, "Equatable protocol is broken: no infix operator declaration for '=='", ()) ERROR(no_equal_overload_for_int,none, "no overload of '==' for Int", ()) +ERROR(broken_coding_key_requirement,none, + "CodingKey protocol is broken: unexpected requirement", ()) +ERROR(broken_codable_requirement,none, + "Codable protocol is broken: unexpected requirement", ()) // Dynamic Self ERROR(dynamic_self_non_method,none, diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 46e21dc432a33..93e487c82458c 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -29,24 +29,39 @@ IDENTIFIER(Any) IDENTIFIER(atIndexedSubscript) IDENTIFIER_(bridgeToObjectiveC) IDENTIFIER_WITH_NAME(code_, "_code") +IDENTIFIER(Codable) +IDENTIFIER(CodingKeys) +IDENTIFIER(container) IDENTIFIER(CoreGraphics) IDENTIFIER(CoreMedia) IDENTIFIER(CGFloat) IDENTIFIER(CVarArg) IDENTIFIER(Darwin) IDENTIFIER(dealloc) +IDENTIFIER(decode) +IDENTIFIER(Decoder) +IDENTIFIER(decoder) IDENTIFIER(deinit) IDENTIFIER(Element) +IDENTIFIER(encode) +IDENTIFIER(Encoder) +IDENTIFIER(encoder) IDENTIFIER(error) IDENTIFIER(forKeyedSubscript) IDENTIFIER(Foundation) +IDENTIFIER(forKey) +IDENTIFIER(from) IDENTIFIER(fromRaw) IDENTIFIER(hashValue) IDENTIFIER(init) IDENTIFIER(initialize) IDENTIFIER(initStorage) IDENTIFIER(initialValue) +IDENTIFIER(intValue) IDENTIFIER(Key) +IDENTIFIER(KeyedDecodingContainer) +IDENTIFIER(KeyedEncodingContainer) +IDENTIFIER(keyedBy) IDENTIFIER(makeIterator) IDENTIFIER(Iterator) IDENTIFIER(load) @@ -69,10 +84,16 @@ IDENTIFIER(setObject) IDENTIFIER(simd) IDENTIFIER(some) IDENTIFIER(storage) +IDENTIFIER(stringValue) IDENTIFIER(subscript) +IDENTIFIER(super) +IDENTIFIER(superDecoder) +IDENTIFIER(superEncoder) IDENTIFIER(SwiftObject) +IDENTIFIER(to) IDENTIFIER(toRaw) IDENTIFIER(Type) +IDENTIFIER(type) IDENTIFIER(Value) IDENTIFIER(value) IDENTIFIER_WITH_NAME(value_, "_value") diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 6fd321d915979..ffd5a20c59cad 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -60,6 +60,8 @@ PROTOCOL_(ErrorCodeProtocol) PROTOCOL(OptionSet) PROTOCOL_(BridgedNSError) PROTOCOL_(BridgedStoredNSError) +PROTOCOL(CodingKey) +PROTOCOL(Codable) PROTOCOL_(ObjectiveCBridgeable) PROTOCOL_(DestructorSafeContainer) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 545630b07f42c..3243e798acc54 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -743,7 +743,9 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { // are in the Foundation module. if (kind == KnownProtocolKind::BridgedNSError || kind == KnownProtocolKind::BridgedStoredNSError || - kind == KnownProtocolKind::ErrorCodeProtocol) { + kind == KnownProtocolKind::ErrorCodeProtocol || + kind == KnownProtocolKind::CodingKey || + kind == KnownProtocolKind::Codable) { ModuleDecl *foundation = const_cast(this)->getLoadedModule(Id_Foundation); if (!foundation) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 246a1dba41d2a..ab77ae58e47ed 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2092,9 +2092,39 @@ bool NominalTypeDecl::derivesProtocolConformance(ProtocolDecl *protocol) const { case KnownProtocolKind::BridgedNSError: return isObjC() && enumDecl->hasOnlyCasesWithoutAssociatedValues(); + // Enums without associated values and enums with a raw type of String + // or Int can explicitly derive CodingKey conformance. + case KnownProtocolKind::CodingKey: { + Type rawType = enumDecl->getRawType(); + if (rawType) { + auto parentDC = enumDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto nominal = rawType->getAnyNominal(); + return nominal == C.getStringDecl() || nominal == C.getIntDecl(); + } else { + // Empty enums are allowed to conform as well. + return enumDecl->getAllElements().empty() || + enumDecl->hasOnlyCasesWithoutAssociatedValues(); + } + } + default: return false; } + } else if (isa(this) || isa(this)) { + // Structs and classes can explicitly derive Codable conformance. + if (*knownProtocol == KnownProtocolKind::Codable) { + // FIXME: This is not actually correct. We cannot promise to always + // provide a witness here for all structs and classes. Unfortunately, + // figuring out whether this is actually possible requires much more + // context -- a TypeChecker and the parent decl context at least -- and is + // tightly coupled to the logic within DerivedConformance. + // This unfortunately means that we expect a witness even if one will not + // be produced, which requires DerivedConformance::deriveCodable to output + // its own diagnostics. + return true; + } } return false; } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index c22f9d5c2c35e..3593bdf9f4bd3 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5548,6 +5548,8 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::BridgedNSError: case KnownProtocolKind::BridgedStoredNSError: case KnownProtocolKind::ErrorCodeProtocol: + case KnownProtocolKind::CodingKey: + case KnownProtocolKind::Codable: return SpecialProtocol::None; } diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 7d87dfa8b7bcf..a4417917a10df 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -16,6 +16,8 @@ add_swift_library(swiftSema STATIC ConstraintGraph.cpp ConstraintLocator.cpp ConstraintSystem.cpp + DerivedConformanceCodable.cpp + DerivedConformanceCodingKey.cpp DerivedConformanceEquatableHashable.cpp DerivedConformanceError.cpp DerivedConformanceRawRepresentable.cpp diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp new file mode 100644 index 0000000000000..06facc3e89516 --- /dev/null +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -0,0 +1,949 @@ +#include "TypeChecker.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Expr.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/Pattern.h" +#include "swift/AST/Stmt.h" +#include "swift/AST/Types.h" +#include "DerivedConformances.h" + +using namespace swift; +using namespace DerivedConformance; + +// Returns whether the type represented by the given ClassDecl inherits from a +// type which is Codable. +static bool classInheritsCodable(TypeChecker &tc, ClassDecl *type) { + if (!type || !type->hasSuperclass()) + return false; + + ASTContext &C = tc.Context; + auto parentDC = type->getDeclContext(); + auto superType = type->getSuperclass(); + return (bool)tc.conformsToProtocol(superType, + C.getProtocol(KnownProtocolKind::Codable), + parentDC, + ConformanceCheckFlags::Used); +} + +// Looks up a type declaration with the given name in the Foundation module. +// +// Asserts that the Foundation module is loaded, that the given name does not +// refer to more than one type, and that the entity with the given name refers +// to a type instead of a different type of declaration. +static TypeDecl* lookupFoundationTypeDecl(ASTContext &C, Identifier name) { + auto foundationModule = C.getLoadedModule(C.Id_Foundation); + assert(foundationModule && "Foundation module must be loaded."); + + SmallVector results; + foundationModule->lookupMember(results, cast(foundationModule), + DeclName(name), Identifier()); + assert(results.size() == 1 && "Ambiguous/missing type."); + + auto typeDecl = dyn_cast(results[0]); + assert(typeDecl && "Found non-type decl."); + return typeDecl; +} + +// Looks up a type with the given name in the Foundation module. +// +// Asserts that the Foundation module is loaded, that the given name does not +// refer to more than one type, and that the entity with the given name refers +// to a type instead of a different type of declaration. +static Type lookupFoundationType(ASTContext &C, Identifier name) { + return lookupFoundationTypeDecl(C, name)->getDeclaredInterfaceType(); +} + +// Validates that all the variables declared in the given list of declarations +// conform to the Codable protocol. +// +// Produces a diagnostic on the given typechecker for every var which does not +// conform. Calls a success callback for every var which does conform. +template +static bool +validateVarsConformToCodable(TypeChecker &tc, DeclContext *context, + NominalTypeDecl::StoredPropertyRange &vars, + ValidVarCallback &callback) { + auto codableProto = tc.Context.getProtocol(KnownProtocolKind::Codable); + bool allConform = true; + for (auto varDecl : vars) { + // If the decl doesn't yet have a type, we may be seeing it before the type + // checker has gotten around to evaluating its type. For example: + // + // func foo() { + // let b = Bar(from: decoder) // <- evaluates Bar conformance to Codable, + // // forcing derivation + // } + // + // struct Bar : Codable { + // var x: Int // <- we get to valuate x's var decl here, but its type + // // hasn't yet been evaluated + // } + // + // Validate the decl eagerly. + if (!varDecl->hasType()) + tc.validateDecl(varDecl); + + // If the var decl didn't validate, it may still not have a type; confirm it + // has a type before ensuring the type conforms to Codable. + if (varDecl->hasType() && + tc.conformsToProtocol(varDecl->getType(), codableProto, context, + ConformanceCheckFlags::Used)) { + callback(varDecl); + } else { + // TODO: We should produce a diagnostic note here explaining that we found + // a var not conforming to Codable. + allConform = false; + } + } + + return allConform; +} + +// Validates the given CodingKeys enum decl by ensuring its cases are a 1-to-1 +// match with the stored vars of the given type. +static bool +validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, + NominalTypeDecl *type) { + // Look through all var decls in the given type. + // * Filter out lazy/computed vars (currently already done by + // getStoredProperties). + // * Filter out ones which are present in the given decl (by name). + // + // If any of the entries in the CodingKeys decl are not present in the type + // by name, then this decl doesn't match. + // If there are any vars left in the type, then this decl doesn't match. + // + // NOTE: If we change the behavior to ignore vars with default values, then we + // can further filter out the type names to remove those which + // correspond to vars with default values. + llvm::DenseSet names; + + auto storedProperties = type->getStoredProperties(/*skipInaccessible=*/true); + auto validVarCallback = [&names](VarDecl *varDecl) { + names.insert(varDecl->getName()); + }; + + if (!validateVarsConformToCodable(tc, type->getDeclContext(), + storedProperties, validVarCallback)) + return false; + + for (auto elt : codingKeysDecl->getAllElements()) { + auto it = names.find(elt->getName()); + if (it == names.end()) { + // TODO: Produce diagnostic here complaining that the CodingKeys enum + // contains a case which does not correspond to a var. + // TODO: Investigate typo-correction here; perhaps the case name was + // misspelled and we can provide a fix-it. + return false; + } + + names.erase(it); + } + + // TODO: Produce diagnostic here complaining that there are vars which are not + // listed in the CodingKeys enum. + return names.empty(); +} + +// Fetches or synthesizes a CodingKeys type nested in the given type. +// +// If the given type declaration has a nested CodingKeys entity, returns it if +// it is a valid NominalTypeDecl (nullptr otherwise). +// If the given type declaration does not have a nested CodingKeys entity, +// synthesizes a new enum based on the Codable members of the given type +// (nullptr if unable to synthesize). +static std::pair +getOrSynthesizeCodingKeysDecl(TypeChecker &tc, Decl *parentDecl, + NominalTypeDecl *type) { + ASTContext &C = tc.Context; + auto parentDC = cast(parentDecl); + auto codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); + auto typeDC = cast(type); + + auto codingKeysDecls = type->lookupDirect(DeclName(C.Id_CodingKeys)); + if (!codingKeysDecls.empty()) { + // Only ill-formed code would produce multiple results for this lookup. + // This would get diagnosed later anyway, so we're free to only look at the + // first result here. + auto result = codingKeysDecls.front(); + + auto typeDecl = dyn_cast(result); + if (!typeDecl) { + // TODO: Produce a diagnostic complaining that the "CodingKeys" entity we + // found is not a type. + return {nullptr, /* attempted to synthesize? */false}; + } + + // Ensure that the type we found conforms to the CodingKey protocol. + auto type = typeDecl->getDeclaredInterfaceType(); + if (!tc.conformsToProtocol(type, codingKeyProto, parentDC, + ConformanceCheckFlags::Used)) { + // TODO: Produce a diagnostic complaining that the "CodingKeys" entity we + // found does not conform to CodingKey. + return {nullptr, /* attempted to synthesize? */false}; + } + + // CodingKeys should eventually be a nominal type. If it's a typealias, + // we'll need to follow it. + auto codingKeysDecl = dyn_cast(result); + if (auto typealias = dyn_cast(result)) { + // TODO: Do we have to follow through multiple layers of typealiases + // here? Or will getCanonicalType() do that for us? + auto canType = type->getCanonicalType(); + assert(canType); + + codingKeysDecl = type->getNominalOrBoundGenericNominal(); + } + + if (!codingKeysDecl) { + // We ended up with something that's not a nominal type? Need to look + // into how this would happen. + // TODO: Produce a diagnostic complaining that we cannot derive Codable + // with a CodingKeys enum that is not a nominal type. + return {nullptr, /* attempted to synthesize? */false}; + } + + // Didn't synthesize this decl. + return {codingKeysDecl, /* attempted to synthesize? */false}; + } + + // The type does not have a CodingKeys enum. We can synthesize one here. + // We want to look through all the var declarations of this type to create + // enum cases based on those var names. + auto proto = C.getProtocol(KnownProtocolKind::CodingKey); + TypeLoc protoTypeLoc[1] = { TypeLoc::withoutLoc(proto->getDeclaredType()) }; + MutableArrayRef inherited = C.AllocateCopy(protoTypeLoc); + + auto enumDecl = new (C) EnumDecl(SourceLoc(), C.Id_CodingKeys, SourceLoc(), + inherited, nullptr, typeDC); + enumDecl->setImplicit(); + enumDecl->setAccessibility(Accessibility::Private); + + auto enumDC = cast(enumDecl); + auto mutableEnumDC = cast(enumDecl); + + // For classes which inherit from something Codable, we provide case `super` + // as the first key (to be used in encoding super). + auto classDecl = dyn_cast(type); + if (classInheritsCodable(tc, classDecl)) { + // TODO: Ensure the class doesn't already have or inherit a variable named + // "`super`"; otherwise we will generate an invalid enum. In that case, + // diagnose and bail. + auto super = new (C) EnumElementDecl(SourceLoc(), C.Id_super, TypeLoc(), + /*HasArgumentType=*/false, + SourceLoc(), nullptr, enumDC); + super->setImplicit(); + mutableEnumDC->addMember(super); + } + + // Each of these vars needs a case in the enum. For each var decl, if the type + // conforms to Codable, add it to the enum. + auto storedProperties = type->getStoredProperties(/*skipInaccessible=*/true); + auto validVarCallback = [&C, &enumDC, &mutableEnumDC](VarDecl *varDecl) { + auto elt = new (C) EnumElementDecl(SourceLoc(), varDecl->getName(), + TypeLoc(), /*HasArgumentType=*/false, + SourceLoc(), nullptr, + enumDC); + elt->setImplicit(); + mutableEnumDC->addMember(elt); + }; + + if (!validateVarsConformToCodable(tc, parentDC, storedProperties, + validVarCallback)) + return {nullptr, /* attempted to synthesize? */true}; + + // Forcibly derive conformance to CodingKey. + tc.checkConformancesInContext(enumDC, mutableEnumDC); + + // "Synthesized" a null enum decl. + return {enumDecl, /* attempted to synthesize? */true}; +} + +// Creates a new var decl representing +// +// let container : containerBase +// +// containerBase is the name of the type to use as the base (either +// KeyedEncodingContainer or KeyedDecodingContainer). +static VarDecl* +createKeyedContainer(ASTContext &C, DeclContext *DC, Identifier containerBase, + Type keyType) { + // Look up Keyed*Container + auto keyedContainerDecl = + cast(lookupFoundationTypeDecl(C, containerBase)); + + // Bind Keyed*Container to Keyed*Container + Type boundType[1] = { keyType }; + auto containerType = BoundGenericType::get(keyedContainerDecl, Type(), + C.AllocateCopy(boundType)); + + // let container : Keyed*Container + auto containerDecl = new (C) VarDecl(/*IsStatic=*/false, /*IsLet=*/true, + /*IsCaptureList=*/false, SourceLoc(), + C.Id_container, containerType, DC); + containerDecl->setImplicit(); + containerDecl->setInterfaceType(containerType); + return containerDecl; +} + +static CallExpr* +createContainerKeyedByCall(ASTContext &C, DeclContext *DC, Expr *base, + Type returnType, NominalTypeDecl *param) { + // (keyedBy:) + auto keyedByDecl = new (C) ParamDecl(/*IsLet=*/true, SourceLoc(), + SourceLoc(), C.Id_keyedBy, + SourceLoc(), C.Id_keyedBy, + returnType, DC); + keyedByDecl->setImplicit(); + keyedByDecl->setInterfaceType(returnType); + + // container(keyedBy:) method name + auto paramList = ParameterList::createWithoutLoc(keyedByDecl); + DeclName callName(C, C.Id_container, paramList); + + // base.container(keyedBy:) expr + auto unboundCall = new (C) UnresolvedDotExpr(base, SourceLoc(), callName, + DeclNameLoc(), + /*Implicit=*/true); + + // CodingKeys.self expr + auto codingKeysExpr = new (C) DeclRefExpr(ConcreteDeclRef(param), + DeclNameLoc(), /*Implicit=*/true); + auto codingKeysMetaTypeExpr = new (C) DotSelfExpr(codingKeysExpr, SourceLoc(), + SourceLoc()); + + // Full bound base.container(keyedBy: CodingKeys.self) call + Expr* args[1] = { codingKeysMetaTypeExpr }; + Identifier argLabels[1] = { C.Id_keyedBy }; + return CallExpr::createImplicit(C, unboundCall, C.AllocateCopy(args), + C.AllocateCopy(argLabels)); +} + +static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { + // struct Foo : Codable { + // var x: Int + // var y: String + // + // // Already derived by this point if possible. + // @derived enum CodingKeys : CodingKey { + // case x + // case y + // } + // + // @derived func encode(to encoder: Encoder) throws { + // let container = encoder.container(keyedBy: CodingKeys.self) + // try container.encode(x, forKey: .x) + // try container.encode(y, forKey: .y) + // } + // } + + // The enclosing type decl. + auto typeDecl = cast(encodeDecl->getDeclContext()); + + auto funcDC = cast(encodeDecl); + ASTContext &C = funcDC->getASTContext(); + auto &tc = *(TypeChecker *)C.getLazyResolver(); + + // We'll want the CodingKeys enum for this type. + NominalTypeDecl *codingKeysDecl; + bool derived; + std::tie(codingKeysDecl, derived) = getOrSynthesizeCodingKeysDecl(tc, + typeDecl, + typeDecl); + + // We should have bailed already if: + // a) The type does not have CodingKeys + assert(codingKeysDecl && "Missing CodingKeys decl."); + // b) We had to derive it right here + assert(!derived && "Had to derive CodingKeys type too late."); + // c) The type is not an enum + auto codingKeysEnum = cast(codingKeysDecl); + + SmallVector statements; + + // Generate a reference to containerExpr ahead of time in case there are no + // properties to encode or decode, but the type is a class which inherits from + // something Codable and needs to encode super. + + // let container : KeyedEncodingContainer + auto codingKeysType = codingKeysEnum->getDeclaredType(); + auto containerDecl = createKeyedContainer(C, funcDC, + C.Id_KeyedEncodingContainer, + codingKeysType); + + auto containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), + DeclNameLoc(), /*Implicit=*/true, + AccessSemantics::DirectToStorage); + + auto enumElements = codingKeysEnum->getAllElements(); + if (!enumElements.empty()) { + // Need to generate + // `let container = encoder.container(keyedBy: CodingKeys.self)` + // `let container` (containerExpr) is generated above. + + // encoder + auto encoderParam = encodeDecl->getParameterList(1)->get(0); + auto encoderExpr = new (C) DeclRefExpr(ConcreteDeclRef(encoderParam), + DeclNameLoc(), /*Implicit=*/true); + + // Bound encoder.container(keyedBy: CodingKeys.self) call + auto containerType = containerDecl->getInterfaceType(); + auto callExpr = createContainerKeyedByCall(C, funcDC, encoderExpr, + containerType, codingKeysEnum); + + // Full `let container = encoder.container(keyedBy: CodingKeys.self)` + // binding. + auto containerPattern = new (C) NamedPattern(containerDecl, + /*implicit=*/true); + auto bindingDecl = PatternBindingDecl::create(C, SourceLoc(), + StaticSpellingKind::None, + SourceLoc(), containerPattern, + callExpr, funcDC); + statements.push_back(bindingDecl); + statements.push_back(containerDecl); + + // Now need to generate `try container.encode(x, forKey: .x)` for all + // existing properties. + for (auto elt : enumElements) { + // Only ill-formed code would produce multiple results for this lookup. + // This would get diagnosed later anyway, so we're free to only look at + // the first result here. + auto matchingVars = typeDecl->lookupDirect(DeclName(elt->getName())); + + // self.x + auto selfRef = createSelfDeclRef(encodeDecl); + auto varExpr = new (C) MemberRefExpr(selfRef, SourceLoc(), + ConcreteDeclRef(matchingVars[0]), + DeclNameLoc(), /*Implicit=*/true); + + // CodingKeys.x + auto eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*implicit=*/true); + auto metaTyRef = TypeExpr::createImplicit(codingKeysType, C); + auto keyExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef); + + // encode(_:forKey:) + SmallVector argNames{Identifier(), C.Id_forKey}; + DeclName name(C, C.Id_encode, argNames); + auto encodeCall = new (C) UnresolvedDotExpr(containerExpr, SourceLoc(), + name, DeclNameLoc(), + /*Implicit=*/true); + + // container.encode(self.x, forKey: CodingKeys.x) + Expr* args[2] = { varExpr, keyExpr }; + auto callExpr = CallExpr::createImplicit(C, encodeCall, + C.AllocateCopy(args), + C.AllocateCopy(argNames)); + + // try container.encode(self.x, forKey: CodingKeys.x) + auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); + statements.push_back(tryExpr); + } + } + + // Classes which inherit from something Codable should encode super as well. + auto classDecl = dyn_cast(typeDecl); + if (classInheritsCodable(tc, classDecl)) { + // Need to generate `try super.encode(to: container.superEncoder())` + + // superEncoder() + auto method = new (C) UnresolvedDeclRefExpr(DeclName(C.Id_superEncoder), + DeclRefKind::Ordinary, + DeclNameLoc()); + + // container.superEncoder() + auto superEncoderRef = new (C) DotSyntaxCallExpr(containerExpr, SourceLoc(), + method); + + // encode(to:) expr + auto encodeDeclRef = new (C) DeclRefExpr(ConcreteDeclRef(encodeDecl), + DeclNameLoc(), /*Implicit=*/true); + + // super + auto superRef = new (C) SuperRefExpr(encodeDecl->getImplicitSelfDecl(), + SourceLoc(), /*Implicit=*/true); + + // super.encode(to:) + auto encodeCall = new (C) DotSyntaxCallExpr(superRef, SourceLoc(), + encodeDeclRef); + + // super.encode(to: container.superEncoder()) + Expr* args[1] = { superEncoderRef }; + Identifier argLabels[1] = { C.Id_to }; + auto callExpr = CallExpr::createImplicit(C, encodeCall, + C.AllocateCopy(args), + C.AllocateCopy(argLabels)); + + // try super.encode(to: container.superEncoder()) + auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); + statements.push_back(tryExpr); + } + + auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(), + /*implicit=*/true); + encodeDecl->setBody(body); +} + +static ValueDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, + NominalTypeDecl *type) { + ASTContext &C = tc.Context; + auto typeDC = cast(type); + + // Expected type: (Self) -> (Encoder) throws -> () + // Constructed as: func type + // input: Self + // throws + // output: function type + // input: Encoder + // output: () + // Create from the inside out: + + // (to: Encoder) + auto encoderType = lookupFoundationType(C, C.Id_Encoder); + auto inputTypeElt = TupleTypeElt(encoderType, C.Id_to); + auto inputType = TupleType::get(ArrayRef(inputTypeElt), C); + + // throws + auto extInfo = FunctionType::ExtInfo(FunctionTypeRepresentation::Swift, + /*Throws=*/true); + // () + auto returnType = TupleType::getEmpty(C); + + // (to: Encoder) throws -> () + auto innerType = FunctionType::get(inputType, returnType, extInfo); + + // Params: (self [implicit], Encoder) + auto selfDecl = ParamDecl::createSelf(SourceLoc(), typeDC); + auto encoderParam = new (C) ParamDecl(/*isLet=*/true, SourceLoc(), + SourceLoc(), C.Id_to, + SourceLoc(), C.Id_encoder, + encoderType, typeDC); + encoderParam->setInterfaceType(encoderType); + + ParameterList *params[] = { + ParameterList::createWithoutLoc(selfDecl), + ParameterList::createWithoutLoc(encoderParam) + }; + + // Func name: encode(to: Encoder) + DeclName name(C, C.Id_encode, params[1]); + auto encodeDecl = FuncDecl::create(C, SourceLoc(), StaticSpellingKind::None, + SourceLoc(), name, SourceLoc(), + /*Throws=*/true, SourceLoc(), + SourceLoc(), nullptr, params, + TypeLoc::withoutLoc(returnType), typeDC); + encodeDecl->setImplicit(); + encodeDecl->setBodySynthesizer(deriveBodyCodable_encode); + + // This method should be marked as 'override' for classes inheriting Codable + // conformance from a parent class. + if (auto classDecl = dyn_cast(type)) { + if (classInheritsCodable(tc, classDecl)) { + auto attr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); + encodeDecl->getAttrs().add(attr); + } + } + + // Evaluate the type of Self in (Self) -> (Encoder) throws -> (). + Type selfType = typeDC->getDeclaredInterfaceType(); + Type interfaceType; + if (auto sig = typeDC->getGenericSignatureOfContext()) { + // Evaluate the below, but in a generic environment (if Self is generic). + encodeDecl->setGenericEnvironment( + typeDC->getGenericEnvironmentOfContext()); + interfaceType = GenericFunctionType::get(sig, selfType, innerType, + FunctionType::ExtInfo()); + } else { + // (Self) -> innerType == (Encoder) throws -> () + interfaceType = FunctionType::get(selfType, innerType); + } + + encodeDecl->setInterfaceType(interfaceType); + encodeDecl->setAccessibility(std::max(type->getFormalAccess(), + Accessibility::Internal)); + + // If the type was not imported, the derived conformance is either from the + // type itself or an extension, in which case we will emit the declaration + // normally. + if (type->hasClangNode()) + tc.Context.addExternalDecl(encodeDecl); + + cast(type)->addMember(encodeDecl); + return encodeDecl; + +} + +static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { + // struct Foo : Codable { + // var x: Int + // var y: String + // + // // Already derived by this point if possible. + // @derived enum CodingKeys : CodingKey { + // case x + // case y + // } + // + // @derived init(from decoder: Decoder) throws { + // let container = try decoder.container(keyedBy: CodingKeys.self) + // x = try container.decode(Type.self, forKey: .x) + // y = try container.decode(Type.self, forKey: .y) + // } + // } + + // The enclosing type decl. + auto typeDecl = cast(initDecl->getDeclContext()); + + auto funcDC = cast(initDecl); + ASTContext &C = funcDC->getASTContext(); + auto &tc = *(TypeChecker *)C.getLazyResolver(); + + // We'll want the CodingKeys enum for this type. + NominalTypeDecl *codingKeysDecl; + bool derived; + std::tie(codingKeysDecl, derived) = getOrSynthesizeCodingKeysDecl(tc, + typeDecl, + typeDecl); + + // We should have bailed already if: + // a) The type does not have CodingKeys + assert(codingKeysDecl && "Missing CodingKeys decl."); + // b) We had to derive it right here + assert(!derived && "Had to derive CodingKeys type too late."); + // c) The type is not an enum + auto codingKeysEnum = cast(codingKeysDecl); + + // Generate a reference to containerExpr ahead of time in case there are no + // properties to encode or decode, but the type is a class which inherits from + // something Codable and needs to decode super. + + // let container : KeyedDecodingContainer + auto codingKeysType = codingKeysEnum->getDeclaredType(); + auto containerDecl = createKeyedContainer(C, funcDC, + C.Id_KeyedEncodingContainer, + codingKeysType); + + auto containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), + DeclNameLoc(), /*Implicit=*/true, + AccessSemantics::DirectToStorage); + + SmallVector statements; + auto enumElements = codingKeysEnum->getAllElements(); + if (!enumElements.empty()) { + // Need to generate + // `let container = try decoder.container(keyedBy: CodingKeys.self)` + // `let container` (containerExpr) is generated above. + + // decoder + auto decoderParam = initDecl->getParameterList(1)->get(0); + auto decoderExpr = new (C) DeclRefExpr(ConcreteDeclRef(decoderParam), + DeclNameLoc(), /*Implicit=*/true); + + // Bound decoder.container(keyedBy: CodingKeys.self) call + auto containerType = containerDecl->getInterfaceType(); + auto callExpr = createContainerKeyedByCall(C, funcDC, decoderExpr, + containerType, codingKeysEnum); + + // try decoder.container(keyedBy: CodingKeys.self) + auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*implicit=*/true); + + // Full `let container = decoder.container(keyedBy: CodingKeys.self)` + // binding. + auto containerPattern = new (C) NamedPattern(containerDecl, + /*implicit=*/true); + auto bindingDecl = PatternBindingDecl::create(C, SourceLoc(), + StaticSpellingKind::None, + SourceLoc(), containerPattern, + tryExpr, funcDC); + statements.push_back(bindingDecl); + statements.push_back(containerDecl); + + // Now need to generate `x = try container.encode(Type.self, forKey: .x)` + // for all existing properties. + for (auto elt : enumElements) { + // TODO: Don't decode a let var that has a default value. + // Only ill-formed code would produce multiple results for this lookup. + // This would get diagnosed later anyway, so we're free to only look at + // the first result here. + auto matchingVars = typeDecl->lookupDirect(DeclName(elt->getName())); + auto varDecl = cast(matchingVars[0]); + + // Type.self (where Type === type(of: x) + auto varType = varDecl->getType(); + auto metaTyRef = TypeExpr::createImplicit(varType, C); + auto typeExpr = new (C) DotSelfExpr(metaTyRef, SourceLoc(), SourceLoc(), + varType); + + // CodingKeys.x + auto eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*implicit=*/true); + metaTyRef = TypeExpr::createImplicit(codingKeysType, C); + auto keyExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef); + + // container.decode(_:forKey:) + SmallVector argNames{Identifier(), C.Id_forKey}; + DeclName name(C, C.Id_decode, argNames); + auto decodeCall = new (C) UnresolvedDotExpr(containerExpr, SourceLoc(), + name, DeclNameLoc(), + /*Implicit=*/true); + + // container.decode(Type.self, forKey: CodingKeys.x) + Expr* args[2] = { typeExpr, keyExpr }; + auto callExpr = CallExpr::createImplicit(C, decodeCall, + C.AllocateCopy(args), + C.AllocateCopy(argNames)); + + // try container.decode(Type.self, forKey: CodingKeys.x) + auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); + + auto selfRef = createSelfDeclRef(initDecl); + auto varExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), + DeclName(varDecl->getName()), + DeclNameLoc(), + /*implicit=*/true); + auto assignExpr = new (C) AssignExpr(varExpr, SourceLoc(), tryExpr, + /*Implicit=*/true); + statements.push_back(assignExpr); + } + } + + // Classes which inherit from something Codable should decode super as well. + auto classDecl = dyn_cast(typeDecl); + if (classInheritsCodable(tc, classDecl)) { + // Need to generate `try super.init(from: container.superDecoder())` + + // superDecoder() + auto method = new (C) UnresolvedDeclRefExpr(DeclName(C.Id_superDecoder), + DeclRefKind::Ordinary, + DeclNameLoc()); + + // container.superDecoder() + auto superDecoderRef = new (C) DotSyntaxCallExpr(containerExpr, SourceLoc(), + method); + + // init(from:) expr + auto initDeclRef = new (C) DeclRefExpr(ConcreteDeclRef(initDecl), + DeclNameLoc(), /*Implicit=*/true); + + // super + auto superRef = new (C) SuperRefExpr(initDecl->getImplicitSelfDecl(), + SourceLoc(), /*Implicit=*/true); + + // super.init(from:) + auto decodeCall = new (C) DotSyntaxCallExpr(superRef, SourceLoc(), + initDeclRef); + + // super.decode(from: container.superDecoder()) + Expr* args[1] = { superDecoderRef }; + Identifier argLabels[1] = { C.Id_from }; + auto callExpr = CallExpr::createImplicit(C, decodeCall, + C.AllocateCopy(args), + C.AllocateCopy(argLabels)); + + // try super.init(from: container.superDecoder()) + auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); + statements.push_back(tryExpr); + } + + auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(), + /*implicit=*/true); + initDecl->setBody(body); +} + +static ValueDecl *deriveCodable_init(TypeChecker &tc, Decl *parentDecl, + NominalTypeDecl *type) { + ASTContext &C = tc.Context; + auto typeDC = cast(type); + + // Expected type: (Self) -> (Decoder) throws -> (Self) + // Constructed as: func type + // input: Self + // throws + // output: function type + // input: Encoder + // output: Self + // Compute from the inside out: + + // (from: Decoder) + auto decoderType = lookupFoundationType(C, C.Id_Decoder); + auto inputTypeElt = TupleTypeElt(decoderType, C.Id_from); + auto inputType = TupleType::get(ArrayRef(inputTypeElt), C); + + // throws + auto extInfo = FunctionType::ExtInfo(FunctionTypeRepresentation::Swift, + /*Throws=*/true); + + // (Self) + auto returnType = typeDC->getDeclaredInterfaceType(); + + // (from: Decoder) throws -> (Self) + Type innerType = FunctionType::get(inputType, returnType, extInfo); + + // Params: (self [implicit], Decoder) + // self should be inout if the type is a value type; not inout otherwise. + auto inOut = !isa(type); + auto selfDecl = ParamDecl::createSelf(SourceLoc(), typeDC, + /*isStatic=*/false, + /*isInOut=*/inOut); + auto decoderParamDecl = new (C) ParamDecl(/*isLet=*/true, SourceLoc(), + SourceLoc(), C.Id_from, + SourceLoc(), C.Id_decoder, + decoderType, typeDC); + decoderParamDecl->setImplicit(); + decoderParamDecl->setInterfaceType(decoderType); + + auto paramList = ParameterList::createWithoutLoc(decoderParamDecl); + + // Func name: init(from: Decoder) + DeclName name(C, C.Id_init, paramList); + + auto initDecl = + new (C) ConstructorDecl(name, SourceLoc(), + /*Failability=*/OTK_None, + /*FailabilityLoc=*/SourceLoc(), + /*Throws=*/true, /*ThrowsLoc=*/SourceLoc(), + selfDecl, paramList, + /*GenericParams=*/nullptr, typeDC); + initDecl->setImplicit(); + initDecl->setBodySynthesizer(deriveBodyCodable_init); + + // This constructor should be marked as `required` for non-final classes. + if (isa(type) && type->getAttrs().hasAttribute()) { + auto reqAttr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); + initDecl->getAttrs().add(reqAttr); + } + + Type selfType = initDecl->computeInterfaceSelfType(); + Type selfInitType = initDecl->computeInterfaceSelfType(/*init=*/true); + Type interfaceType; + Type initializerType; + if (auto sig = typeDC->getGenericSignatureOfContext()) { + // Evaluate the below, but in a generic environment (if Self is generic). + initDecl->setGenericEnvironment(typeDC->getGenericEnvironmentOfContext()); + interfaceType = GenericFunctionType::get(sig, selfType, innerType, + FunctionType::ExtInfo()); + initializerType = GenericFunctionType::get(sig, selfInitType, innerType, + FunctionType::ExtInfo()); + } else { + // (Self) -> (Decoder) throws -> (Self) + interfaceType = FunctionType::get(selfType, innerType); + initializerType = FunctionType::get(selfInitType, innerType); + } + + initDecl->setInterfaceType(interfaceType); + initDecl->setInitializerInterfaceType(initializerType); + initDecl->setAccessibility(std::max(type->getFormalAccess(), + Accessibility::Internal)); + + // If the type was not imported, the derived conformance is either from the + // type itself or an extension, in which case we will emit the declaration + // normally. + if (type->hasClangNode()) + tc.Context.addExternalDecl(initDecl); + + cast(type)->addMember(initDecl); + return initDecl; +} + +static bool canSynthesizeCodable(TypeChecker &tc, Decl *parentDecl, + NominalTypeDecl *type) { + // Fetch or synthesize coding keys for this type. We're going to validate them + // against the vars we encounter on this type. + NominalTypeDecl *codingKeysDecl; + bool derived; + std::tie(codingKeysDecl, derived) = getOrSynthesizeCodingKeysDecl(tc, + parentDecl, + type); + + // We attempted to synthesize coding keys for this type but were unable to, + // because of a type mismatch or otherwise. + if (!codingKeysDecl) { + // We better have "derived" this failure; if we couldn't find a CodingKeys + // type, we should have attempted to synthesize something. + assert(derived && "Unable to fetch CodingKeys declaration for type."); + // TODO: Produce a diagnostic here complaining that we couldn't synthesize a + // a CodingKeys type (potentially carry along the diagnostic produced + // in getOrSynthesize...). + return false; + } + + auto codingKeysEnum = dyn_cast(codingKeysDecl); + if (!codingKeysEnum) { + // We better not have synthesized a decl that wasn't an EnumDecl. This check + // isn't strictly necessary, but good to sanity check. + assert(!derived && "Somehow synthesized a non-enum CodingKeys type."); + + // We can't synthesize Codable if the type has a CodingKeys struct, class, + // or other unknown type. + // TODO: Produce a diagnostic here complaining that we couldn't synthesize + // the Codable requirement because we cannot read through non-enum + // CodingKeys. + return false; + } + + // We have a CodingKeys enum decl. If the decl wasn't derived, it needs to be + // validated. + if (!derived && !validateCodingKeysEnum(tc, codingKeysEnum, type)) { + // TODO: Produce a diagnostic here complaining that we couldn't synthesize + // the Codable requirement because of a mismatch in the enum + // (possibly carry forward the diagnostic produced in + // validateCodingKeys...). + return false; + } + + // If we didn't get the chance to synthesize this enum before (currently the + // case as we don't yet have a hook for always deriving the type if need be, + // i.e. with a fake associatedtype requirement) and synthesized it now, we can + // add it to the type. + // We're only going to add it if we're in the context of actually performing + // the synthesis. + if (derived) + cast(type)->addMember(codingKeysEnum); + + return true; +} + +ValueDecl *DerivedConformance::deriveCodable(TypeChecker &tc, + Decl *parentDecl, + NominalTypeDecl *type, + ValueDecl *requirement) { + // We can only synthesize Codable for structs and classes. + if (!isa(type) && !isa(type)) + return nullptr; + + // Check other preconditions for synthesized conformance. + bool canSynthesize = canSynthesizeCodable(tc, parentDecl, type); + + diag::RequirementKind reqKind; + auto name = requirement->getName(); + if (name == tc.Context.Id_init) { + reqKind = diag::RequirementKind::Constructor; + if (canSynthesize) + return deriveCodable_init(tc, parentDecl, type); + } else if (name == tc.Context.Id_encode) { + reqKind = diag::RequirementKind::Func; + if (canSynthesize) + return deriveCodable_encode(tc, parentDecl, type); + } else { + // Unknown requirement. + tc.diagnose(requirement->getLoc(), diag::broken_codable_requirement); + return nullptr; + } + + // Known protocol requirement but could not synthesize. + // FIXME: We have to output at least one error diagnostic here because we + // returned true from NominalTypeDecl::derivesProtocolConformance; if we + // don't, we expect to return a witness here later and crash on an assertion. + // Producing an error stops compilation before then. + auto codableProto = tc.Context.getProtocol(KnownProtocolKind::Codable); + auto codableType = codableProto->getDeclaredType(); + tc.diagnose(type, diag::type_does_not_conform, type->getDeclaredType(), + codableType); + tc.diagnose(requirement, diag::no_witnesses, reqKind, + requirement->getFullName(), codableType, /*AddFixIt=*/false); + return nullptr; +} diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp new file mode 100644 index 0000000000000..91e59c215695f --- /dev/null +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -0,0 +1,615 @@ +#include "TypeChecker.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Expr.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/Pattern.h" +#include "swift/AST/Stmt.h" +#include "swift/AST/Types.h" +#include "DerivedConformances.h" + +using namespace swift; +using namespace DerivedConformance; + +static void setNilReturnBody(AbstractFunctionDecl *funcDecl) { + auto parentDC = funcDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto nilExpr = new (C) NilLiteralExpr(SourceLoc(), /*Implicit=*/true); + auto returnStmt = new (C) ReturnStmt(SourceLoc(), nilExpr); + auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), + SourceLoc()); + funcDecl->setBody(body); +} + +static void setRawValueReturnBody(AbstractFunctionDecl *funcDecl) { + auto parentDC = funcDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto selfRef = createSelfDeclRef(funcDecl); + auto memberRef = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), + C.Id_rawValue, DeclNameLoc(), + /*Implicit=*/true); + + auto returnStmt = new (C) ReturnStmt(SourceLoc(), memberRef); + auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), + SourceLoc()); + funcDecl->setBody(body); +} + +static void +deriveBodyCodingKey_enum_stringValue_raw(AbstractFunctionDecl *strValDecl) { + // enum SomeStringEnum : String { + // case A = "a", B = "b", C = "c" + // @derived var stringValue: String? { + // return self.rawValue + // } + // } + + // Guaranteed to have a rawValue because you cannot declare an enum with a + // raw type but no cases. + setRawValueReturnBody(strValDecl); +} + +static void +deriveBodyCodingKey_enum_stringValue_switch(AbstractFunctionDecl *strValDecl) { + // enum SomeEnum { + // case A, B, C + // @derived var stringValue: String? { + // switch self { + // case A: + // return "A" + // case B: + // return "B" + // case C: + // return "C" + // } + // } + // } + auto parentDC = strValDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); + Type enumType = parentDC->getDeclaredTypeInContext(); + + auto elements = enumDecl->getAllElements(); + if (elements.empty() /* empty enum */) { + setNilReturnBody(strValDecl); + return; + } + + SmallVector cases; + for (auto elt : elements) { + auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), + SourceLoc(), SourceLoc(), + Identifier(), elt, nullptr); + pat->setImplicit(); + + auto labelItem = + CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), nullptr); + + auto returnExpr = new (C) StringLiteralExpr(elt->getNameStr(), + SourceRange(), + /*Implicit=*/true); + auto returnStmt = new (C) ReturnStmt(SourceLoc(), returnExpr); + auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), + SourceLoc()); + cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, + /*HasBoundDecls=*/false, SourceLoc(), + body)); + } + + auto selfRef = createSelfDeclRef(strValDecl); + auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), selfRef, + SourceLoc(), cases, SourceLoc(), C); + auto body = BraceStmt::create(C, SourceLoc(), ASTNode(switchStmt), + SourceLoc()); + strValDecl->setBody(body); +} + +static ValueDecl * +deriveCodingKey_enum_stringValue(TypeChecker &tc, Decl *parentDecl, + EnumDecl *enumDecl) { + ASTContext &C = tc.Context; + auto parentDC = cast(parentDecl); + auto optionalStringType = OptionalType::get(OTK_Optional, + tc.getStringType(parentDC)); + + // Define the getter. + auto getterDecl = declareDerivedPropertyGetter(tc, parentDecl, enumDecl, + optionalStringType, + optionalStringType, + /*isStatic=*/false, + /*isFinal=*/false); + + auto rawType = enumDecl->getRawType(); + if (rawType && rawType->isEqual(tc.getStringType(parentDC))) { + getterDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_stringValue_raw); + } else { + getterDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_stringValue_switch); + } + + // Define the property. + VarDecl *propDecl; + PatternBindingDecl *pbDecl; + std::tie(propDecl, pbDecl) + = declareDerivedReadOnlyProperty(tc, parentDecl, enumDecl, + C.Id_stringValue, + optionalStringType, + optionalStringType, + getterDecl, + /*isStatic=*/false, + /*isFinal=*/false); + + auto dc = cast(parentDecl); + dc->addMember(getterDecl); + dc->addMember(propDecl); + dc->addMember(pbDecl); + return propDecl; +} + +static void +deriveBodyCodingKey_enum_intValue_raw(AbstractFunctionDecl *intValDecl) { + // enum SomeIntEnum : Int { + // case A = 1, B = 2, C = 3 + // @derived var intValue: Int? { + // return self.rawValue + // } + // } + + // Guaranteed to have a rawValue because you cannot declare an enum with a + // raw type but no cases. + setRawValueReturnBody(intValDecl); +} + +void deriveBodyCodingKey_enum_intValue_nil(AbstractFunctionDecl *intValDecl) { + // enum SomeEnum { + // case A, B, C + // @derived var intValue: Int? { + // return nil + // } + // } + setNilReturnBody(intValDecl); +} + +static ValueDecl * +deriveCodingKey_enum_intValue(TypeChecker &tc, Decl *parentDecl, + EnumDecl *enumDecl) { + ASTContext &C = tc.Context; + auto parentDC = cast(parentDecl); + auto optionalIntType = OptionalType::get(OTK_Optional, + tc.getIntType(parentDC)); + + // Define the getter. + auto getterDecl = declareDerivedPropertyGetter(tc, parentDecl, enumDecl, + optionalIntType, + optionalIntType, + /*isStatic=*/false, + /*isFinal=*/false); + + auto rawType = enumDecl->getRawType(); + if (rawType && rawType->isEqual(tc.getIntType(parentDC))) { + getterDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_intValue_raw); + } else { + getterDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_intValue_nil); + } + + // Define the property. + VarDecl *propDecl; + PatternBindingDecl *pbDecl; + std::tie(propDecl, pbDecl) + = declareDerivedReadOnlyProperty(tc, parentDecl, enumDecl, + C.Id_intValue, + optionalIntType, + optionalIntType, + getterDecl, + /*isStatic=*/false, + /*isFinal=*/false); + + auto dc = cast(parentDecl); + dc->addMember(getterDecl); + dc->addMember(propDecl); + dc->addMember(pbDecl); + return propDecl; +} + +static void +deriveBodyCodingKey_enum_init_stringValue_raw(AbstractFunctionDecl *initDecl) { + // enum SomeStringEnum : String { + // case A = "a", B = "b", C = "c" + // @derived init?(stringValue: String) { + // self.init(rawValue: stringValue) + // } + // } + auto parentDC = initDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + // rawValue param to init(rawValue:) + auto rawValueDecl = new (C) ParamDecl(/*IsLet=*/true, SourceLoc(), + SourceLoc(), C.Id_rawValue, + SourceLoc(), C.Id_rawValue, + C.getStringDecl()->getDeclaredType(), + parentDC); + rawValueDecl->setInterfaceType(C.getIntDecl()->getDeclaredType()); + rawValueDecl->setImplicit(); + auto paramList = ParameterList::createWithoutLoc(rawValueDecl); + + // init(rawValue:) constructor name + DeclName ctorName(C, C.Id_init, paramList); + + auto selfRef = createSelfDeclRef(initDecl); + + // self.init(rawValue:) expr + auto initExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), + ctorName, DeclNameLoc(), + /*Implicit=*/true); + + // Get the stringValue param from init(stringValue:). self is the first param + // in the list; stringValue is the second. + auto stringValueParam = initDecl->getParameterList(1)->get(0); + auto stringValueExpr = new (C) DeclRefExpr(ConcreteDeclRef(stringValueParam), + DeclNameLoc(), + /*Implicit=*/true); + + // Bind the stringValue param in self.init(rawValue: stringValue). + ArrayRef args{stringValueExpr}; + ArrayRef argLabels{C.Id_rawValue}; + auto callExpr = CallExpr::createImplicit(C, initExpr, args, argLabels); + + auto body = BraceStmt::create(C, SourceLoc(), ASTNode(callExpr), SourceLoc()); + initDecl->setBody(body); +} + +static void +deriveBodyCodingKey_enum_init_stringValue_switch(AbstractFunctionDecl * + initDecl) { + // enum SomeEnum { + // case A, B, C + // @derived init?(stringValue: String) { + // switch stringValue { + // case "A": + // self = .A + // case "B": + // self = .B + // case "C": + // self = .C + // default: + // return nil + // } + // } + // } + auto parentDC = initDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); + Type enumType = parentDC->getDeclaredTypeInContext(); + + auto elements = enumDecl->getAllElements(); + if (elements.empty() /* empty enum */) { + setNilReturnBody(initDecl); + return; + } + + auto selfRef = createSelfDeclRef(initDecl); + SmallVector cases; + for (auto elt : elements) { + auto litExpr = new (C) StringLiteralExpr(elt->getNameStr(), + SourceRange(), + /*Implicit=*/true); + auto litPat = new (C) ExprPattern(litExpr, /*IsResolved=*/true, + nullptr, nullptr); + litPat->setImplicit(); + + auto labelItem = + CaseLabelItem(/*IsDefault=*/false, litPat, SourceLoc(), nullptr); + + auto eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*Implicit=*/true); + auto metaTyRef = TypeExpr::createImplicit(enumType, C); + auto valueExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef); + + auto assignment = new (C) AssignExpr(selfRef, SourceLoc(), valueExpr, + /*Implicit=*/true); + + auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignment), + SourceLoc()); + cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, + /*HasBoundDecls=*/false, SourceLoc(), + body)); + } + + auto anyPat = new (C) AnyPattern(SourceLoc()); + anyPat->setImplicit(); + auto dfltLabelItem = + CaseLabelItem(/*IsDefault=*/true, anyPat, SourceLoc(), nullptr); + + auto dfltReturnStmt = new (C) FailStmt(SourceLoc(), SourceLoc()); + auto dfltBody = BraceStmt::create(C, SourceLoc(), ASTNode(dfltReturnStmt), + SourceLoc()); + cases.push_back(CaseStmt::create(C, SourceLoc(), dfltLabelItem, + /*HasBoundDecls=*/false, SourceLoc(), + dfltBody)); + + auto stringValueDecl = initDecl->getParameterList(1)->get(0); + auto stringValueRef = new (C) DeclRefExpr(stringValueDecl, DeclNameLoc(), + /*Implicit=*/true); + auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), + stringValueRef, SourceLoc(), cases, + SourceLoc(), C); + auto body = BraceStmt::create(C, SourceLoc(), ASTNode(switchStmt), + SourceLoc()); + initDecl->setBody(body); +} + +static ValueDecl * +deriveCodingKey_enum_init_stringValue(TypeChecker &tc, Decl *parentDecl, + EnumDecl *enumDecl) { + ASTContext &C = tc.Context; + auto parentDC = cast(parentDecl); + auto stringType = tc.getStringType(parentDC); + + auto *selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC, + /*static*/false, /*inout*/true); + + auto *rawDecl = new (C) ParamDecl(/*IsLet*/true, SourceLoc(), SourceLoc(), + C.Id_stringValue, SourceLoc(), + C.Id_stringValue, stringType, parentDC); + rawDecl->setInterfaceType(stringType); + rawDecl->setImplicit(); + auto paramList = ParameterList::createWithoutLoc(rawDecl); + DeclName name(C, C.Id_init, paramList); + + auto initDecl = + new (C) ConstructorDecl(name, SourceLoc(), + /*Failability=*/OTK_Optional, + /*FailabilityLoc=*/SourceLoc(), + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + selfDecl, paramList, + /*GenericParams=*/nullptr, parentDC); + + initDecl->setImplicit(); + + auto rawType = enumDecl->getRawType(); + if (rawType && rawType->isEqual(stringType)) { + initDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_init_stringValue_raw); + } else { + initDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_init_stringValue_switch); + } + + // Compute the type of the initializer. + TupleTypeElt element(stringType, C.Id_stringValue); + TupleTypeElt interfaceElement(stringType, C.Id_stringValue); + auto interfaceArgType = TupleType::get(interfaceElement, C); + + // Compute the interface type of the initializer. + Type retInterfaceType + = OptionalType::get(parentDC->getDeclaredInterfaceType()); + Type interfaceType = FunctionType::get(interfaceArgType, retInterfaceType); + Type selfInterfaceType = initDecl->computeInterfaceSelfType(); + Type selfInitializerInterfaceType + = initDecl->computeInterfaceSelfType(/*init*/ true); + + Type allocIfaceType; + Type initIfaceType; + if (auto sig = parentDC->getGenericSignatureOfContext()) { + initDecl->setGenericEnvironment(parentDC->getGenericEnvironmentOfContext()); + + allocIfaceType = GenericFunctionType::get(sig, selfInterfaceType, + interfaceType, + FunctionType::ExtInfo()); + initIfaceType = GenericFunctionType::get(sig, selfInitializerInterfaceType, + interfaceType, + FunctionType::ExtInfo()); + } else { + allocIfaceType = FunctionType::get(selfInterfaceType, + interfaceType); + initIfaceType = FunctionType::get(selfInitializerInterfaceType, + interfaceType); + } + initDecl->setInterfaceType(allocIfaceType); + initDecl->setInitializerInterfaceType(initIfaceType); + initDecl->setAccessibility(std::max(Accessibility::Internal, + enumDecl->getFormalAccess())); + + // If the enum was not imported, the derived conformance is either from the + // enum itself or an extension, in which case we will emit the declaration + // normally. + if (enumDecl->hasClangNode()) + tc.Context.addExternalDecl(initDecl); + + cast(parentDecl)->addMember(initDecl); + return initDecl; +} + +static void deriveBodyCodingKey_enum_init_intValue_raw(AbstractFunctionDecl * + initDecl) { + // enum SomeIntEnum : Int { + // case A = 1, B = 2, C = 3 + // @derived init?(intValue: Int) { + // self.init(rawValue: intValue) + // } + // } + auto parentDC = initDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + // rawValue param to init(rawValue:) + auto rawValueDecl = new (C) ParamDecl(/*IsLet=*/true, SourceLoc(), + SourceLoc(), C.Id_rawValue, + SourceLoc(), C.Id_rawValue, + C.getIntDecl()->getDeclaredType(), + parentDC); + rawValueDecl->setInterfaceType(C.getIntDecl()->getDeclaredType()); + rawValueDecl->setImplicit(); + auto paramList = ParameterList::createWithoutLoc(rawValueDecl); + + // init(rawValue:) constructor name + DeclName ctorName(C, C.Id_init, paramList); + + auto selfRef = createSelfDeclRef(initDecl); + + // self.init(rawValue:) expr + auto initExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), + ctorName, DeclNameLoc(), + /*Implicit=*/true); + + // Get the intValue param from init(intValue:). self is the first param + // in the list; intValue is the second. + auto intValueParam = initDecl->getParameterList(1)->get(0); + auto intValueExpr = new (C) DeclRefExpr(ConcreteDeclRef(intValueParam), + DeclNameLoc(), + /*Implicit=*/true); + + // Bind the intValue param in self.init(rawValue: intValue). + ArrayRef args{intValueExpr}; + ArrayRef argLabels{C.Id_rawValue}; + auto callExpr = CallExpr::createImplicit(C, initExpr, args, argLabels); + + auto body = BraceStmt::create(C, SourceLoc(), ASTNode(callExpr), SourceLoc()); + initDecl->setBody(body); +} + +static void +deriveBodyCodingKey_enum_init_intValue_nil(AbstractFunctionDecl *initDecl) { + // enum SomeEnum { + // case A, B, C + // @derived init?(intValue: Int) { + // return nil + // } + // } + setNilReturnBody(initDecl); +} + +static ValueDecl * +deriveCodingKey_enum_init_intValue(TypeChecker &tc, Decl *parentDecl, + EnumDecl *enumDecl) { + ASTContext &C = tc.Context; + auto parentDC = cast(parentDecl); + auto intType = tc.getIntType(parentDC); + + auto *selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC, + /*static*/false, /*inout*/true); + + auto *rawDecl = new (C) ParamDecl(/*IsLet*/true, SourceLoc(), SourceLoc(), + C.Id_intValue, SourceLoc(), + C.Id_intValue, intType, parentDC); + rawDecl->setInterfaceType(intType); + rawDecl->setImplicit(); + auto paramList = ParameterList::createWithoutLoc(rawDecl); + DeclName name(C, C.Id_init, paramList); + + auto initDecl = + new (C) ConstructorDecl(name, SourceLoc(), + /*Failability=*/OTK_Optional, + /*FailabilityLoc=*/SourceLoc(), + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + selfDecl, paramList, + /*GenericParams=*/nullptr, parentDC); + + initDecl->setImplicit(); + + auto rawType = enumDecl->getRawType(); + if (rawType && rawType->isEqual(intType)) { + initDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_init_intValue_raw); + } else { + initDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_init_intValue_nil); + } + + // Compute the type of the initializer. + TupleTypeElt element(intType, C.Id_intValue); + TupleTypeElt interfaceElement(intType, C.Id_intValue); + auto interfaceArgType = TupleType::get(interfaceElement, C); + + // Compute the interface type of the initializer. + Type retInterfaceType + = OptionalType::get(parentDC->getDeclaredInterfaceType()); + Type interfaceType = FunctionType::get(interfaceArgType, retInterfaceType); + Type selfInterfaceType = initDecl->computeInterfaceSelfType(); + Type selfInitializerInterfaceType + = initDecl->computeInterfaceSelfType(/*init*/ true); + + Type allocIfaceType; + Type initIfaceType; + if (auto sig = parentDC->getGenericSignatureOfContext()) { + initDecl->setGenericEnvironment(parentDC->getGenericEnvironmentOfContext()); + + allocIfaceType = GenericFunctionType::get(sig, selfInterfaceType, + interfaceType, + FunctionType::ExtInfo()); + initIfaceType = GenericFunctionType::get(sig, selfInitializerInterfaceType, + interfaceType, + FunctionType::ExtInfo()); + } else { + allocIfaceType = FunctionType::get(selfInterfaceType, + interfaceType); + initIfaceType = FunctionType::get(selfInitializerInterfaceType, + interfaceType); + } + initDecl->setInterfaceType(allocIfaceType); + initDecl->setInitializerInterfaceType(initIfaceType); + initDecl->setAccessibility(std::max(Accessibility::Internal, + enumDecl->getFormalAccess())); + + // If the enum was not imported, the derived conformance is either from the + // enum itself or an extension, in which case we will emit the declaration + // normally. + if (enumDecl->hasClangNode()) + tc.Context.addExternalDecl(initDecl); + + cast(parentDecl)->addMember(initDecl); + return initDecl; +} + +static bool +canSynthesizeCodingKey(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) { + // If the enum has a raw type (optional), it must be String or Int. + Type rawType = enumDecl->getRawType(); + if (rawType) { + auto parentDC = cast(parentDecl); + rawType = parentDC->mapTypeIntoContext(rawType); + + ASTContext &C = tc.Context; + auto nominal = rawType->getCanonicalType()->getAnyNominal(); + if (nominal != C.getStringDecl() && nominal != C.getIntDecl()) + return false; + } + + if (!enumDecl->getInherited().empty() && + enumDecl->getInherited().front().isError()) + return false; + + // If it meets all of those requirements, we can synthesize CodingKey + // conformance. + return true; +} + +ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, + Decl *parentDecl, + NominalTypeDecl *type, + ValueDecl *requirement) { + + // We can only synthesize CodingKey for enums. + auto enumDecl = dyn_cast(type); + if (!enumDecl) + return nullptr; + + // Check other preconditions for synthesized conformance. + if (!canSynthesizeCodingKey(tc, parentDecl, enumDecl)) + return nullptr; + + auto name = requirement->getName(); + if (name == tc.Context.Id_stringValue) { + return deriveCodingKey_enum_stringValue(tc, parentDecl, enumDecl); + } else if (name == tc.Context.Id_intValue) { + return deriveCodingKey_enum_intValue(tc, parentDecl, enumDecl); + } else if (name == tc.Context.Id_init) { + auto argumentNames = requirement->getFullName().getArgumentNames(); + if (argumentNames.size() == 1) { + if (argumentNames[0] == tc.Context.Id_stringValue) + return deriveCodingKey_enum_init_stringValue(tc, parentDecl, enumDecl); + else if (argumentNames[0] == tc.Context.Id_intValue) + return deriveCodingKey_enum_init_intValue(tc, parentDecl, enumDecl); + } + } + + tc.diagnose(requirement->getLoc(), + diag::broken_coding_key_requirement); + return nullptr; +} diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 5d0dcbd83fe83..5ab3a6084f371 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -58,6 +58,14 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, if (name.isSimpleName(ctx.Id_nsErrorDomain)) return getRequirement(KnownProtocolKind::BridgedNSError); + // CodingKey.stringValue + if (name.isSimpleName(ctx.Id_stringValue)) + return getRequirement(KnownProtocolKind::CodingKey); + + // CodingKey.intValue + if (name.isSimpleName(ctx.Id_intValue)) + return getRequirement(KnownProtocolKind::CodingKey); + return nullptr; } @@ -66,14 +74,33 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, if (func->isOperator() && name.getBaseName().str() == "==") return getRequirement(KnownProtocolKind::Equatable); + // Codable.encode(to: Encoder) + if (name.isCompoundName() && name.getBaseName() == ctx.Id_encode) { + auto argumentNames = name.getArgumentNames(); + if (argumentNames.size() == 1 && argumentNames[0] == ctx.Id_to) + return getRequirement(KnownProtocolKind::Codable); + } + return nullptr; } // Initializers. - if (isa(requirement)) { + if (auto ctor = dyn_cast(requirement)) { auto argumentNames = name.getArgumentNames(); - if (argumentNames.size() == 1 && argumentNames[0] == ctx.Id_rawValue) - return getRequirement(KnownProtocolKind::RawRepresentable); + if (argumentNames.size() == 1) { + if (argumentNames[0] == ctx.Id_rawValue) + return getRequirement(KnownProtocolKind::RawRepresentable); + + // CodingKey.init?(stringValue:), CodingKey.init?(intValue:) + if (ctor->getFailability() == OTK_Optional && + (argumentNames[0] == ctx.Id_stringValue || + argumentNames[0] == ctx.Id_intValue)) + return getRequirement(KnownProtocolKind::CodingKey); + + // Codable.init(from: Decoder) + if (argumentNames[0] == ctx.Id_from) + return getRequirement(KnownProtocolKind::Codable); + } return nullptr; } @@ -84,6 +111,10 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, if (name.isSimpleName(ctx.Id_RawValue)) return getRequirement(KnownProtocolKind::RawRepresentable); + // Codable.CodingKeys + if (name.isSimpleName(ctx.Id_CodingKeys)) + return getRequirement(KnownProtocolKind::Codable); + return nullptr; } diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 6cfe2e3bad08e..79f3c97cb33c6 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -96,6 +96,22 @@ ValueDecl *deriveBridgedNSError(TypeChecker &tc, NominalTypeDecl *type, ValueDecl *requirement); +/// Derive a CodingKey requirement for an enum type. +/// +/// \returns the derived member, which will also be added to the type. +ValueDecl *deriveCodingKey(TypeChecker &tc, + Decl *parentDecl, + NominalTypeDecl *type, + ValueDecl *requirement); + +/// Derive a Codable requirement for a struct type. +/// +/// \returns the derived member, which will also be added to the type. +ValueDecl *deriveCodable(TypeChecker &tc, + Decl *parentDecl, + NominalTypeDecl *type, + ValueDecl *requirement); + /// Declare a getter for a derived property. FuncDecl *declareDerivedPropertyGetter(TypeChecker &tc, Decl *parentDecl, diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 2dd29038ddd48..5a9bc24d78758 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -6234,6 +6234,12 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, return DerivedConformance::deriveBridgedNSError(*this, Decl, TypeDecl, Requirement); + case KnownProtocolKind::CodingKey: + return DerivedConformance::deriveCodingKey(*this, Decl, TypeDecl, Requirement); + + case KnownProtocolKind::Codable: + return DerivedConformance::deriveCodable(*this, Decl, TypeDecl, Requirement); + default: return nullptr; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 03d9332f9d7ca..fea7c60b866e8 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -99,6 +99,9 @@ static Type getStdlibType(TypeChecker &TC, Type &cached, DeclContext *dc, Type TypeChecker::getStringType(DeclContext *dc) { return ::getStdlibType(*this, StringType, dc, "String"); } +Type TypeChecker::getIntType(DeclContext *dc) { + return ::getStdlibType(*this, IntType, dc, "Int"); +} Type TypeChecker::getInt8Type(DeclContext *dc) { return ::getStdlibType(*this, Int8Type, dc, "Int8"); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index f2c31b4ea5c54..1f18a89a47656 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -715,6 +715,7 @@ class TypeChecker final : public LazyResolver { Type ImageLiteralType; Type FileReferenceLiteralType; Type StringType; + Type IntType; Type Int8Type; Type UInt8Type; Type NSObjectType; @@ -805,6 +806,7 @@ class TypeChecker final : public LazyResolver { Type getOptionalType(SourceLoc loc, Type elementType); Type getImplicitlyUnwrappedOptionalType(SourceLoc loc, Type elementType); Type getStringType(DeclContext *dc); + Type getIntType(DeclContext *dc); Type getInt8Type(DeclContext *dc); Type getUInt8Type(DeclContext *dc); Type getNSObjectType(DeclContext *dc); diff --git a/test/decl/protocol/special/coding/Inputs/class_codable_simple_multi1.swift b/test/decl/protocol/special/coding/Inputs/class_codable_simple_multi1.swift new file mode 100644 index 0000000000000..b95dc24d6f906 --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/class_codable_simple_multi1.swift @@ -0,0 +1,25 @@ +// REQUIRES: objc_interop + +import Foundation + +// Simple classes with all Codable properties should get derived conformance to +// Codable. +class SimpleClass : Codable { + var x: Int = 1 + var y: Double = .pi + static var z: String = "foo" + + // These lines have to be within the SimpleClass type because CodingKeys + // should be private. + func foo() { + // They should receive a synthesized CodingKeys enum. + let _ = SimpleClass.CodingKeys.self + + // The enum should have a case for each of the vars. + let _ = SimpleClass.CodingKeys.x + let _ = SimpleClass.CodingKeys.y + + // Static vars should not be part of the CodingKeys enum. + let _ = SimpleClass.CodingKeys.z // expected-error {{type 'SimpleClass.CodingKeys' has no member 'z'}} + } +} diff --git a/test/decl/protocol/special/coding/Inputs/class_codable_simple_multi2.swift b/test/decl/protocol/special/coding/Inputs/class_codable_simple_multi2.swift new file mode 100644 index 0000000000000..0a8d5d2951912 --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/class_codable_simple_multi2.swift @@ -0,0 +1,10 @@ +// These are wrapped in a dummy function to avoid binding a global variable. +func foo() { + // They should receive synthesized init(from:) and an encode(to:). + let _ = SimpleClass.init(from:) + let _ = SimpleClass.encode(to:) + + // The synthesized CodingKeys type should not be accessible from outside the + // class. + let _ = SimpleClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} +} diff --git a/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi1.swift b/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi1.swift new file mode 100644 index 0000000000000..4286f70a985a0 --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi1.swift @@ -0,0 +1,11 @@ +enum NoRawTypeKey { + case a, b, c +} + +enum StringKey : String { + case a = "A", b, c = "Foo" +} + +enum IntKey : Int { + case a = 3, b, c = 1 +} diff --git a/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi2.swift b/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi2.swift new file mode 100644 index 0000000000000..b72cda761dd3a --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi2.swift @@ -0,0 +1,7 @@ +// REQUIRES: objc_interop + +import Foundation + +extension NoRawTypeKey : CodingKey {} +extension StringKey : CodingKey {} +extension IntKey : CodingKey {} diff --git a/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi3.swift b/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi3.swift new file mode 100644 index 0000000000000..e56a15e194e02 --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi3.swift @@ -0,0 +1,16 @@ +func foo() { + let _ = NoRawTypeKey.a.stringValue + let _ = NoRawTypeKey(stringValue: "a") + let _ = NoRawTypeKey.a.intValue + let _ = NoRawTypeKey(intValue: 0) + + let _ = StringKey.a.stringValue + let _ = StringKey(stringValue: "A") + let _ = StringKey.a.intValue + let _ = StringKey(intValue: 0) + + let _ = IntKey.a.stringValue + let _ = IntKey(stringValue: "a") + let _ = IntKey.a.intValue + let _ = IntKey(intValue: 3) +} diff --git a/test/decl/protocol/special/coding/Inputs/enum_coding_key_multi1.swift b/test/decl/protocol/special/coding/Inputs/enum_coding_key_multi1.swift new file mode 100644 index 0000000000000..82e567bc98610 --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/enum_coding_key_multi1.swift @@ -0,0 +1,15 @@ +// REQUIRES: objc_interop + +import Foundation + +enum NoRawTypeKey : CodingKey { + case a, b, c +} + +enum StringKey : String, CodingKey { + case a = "A", b, c = "Foo" +} + +enum IntKey : Int, CodingKey { + case a = 3, b, c = 1 +} diff --git a/test/decl/protocol/special/coding/Inputs/enum_coding_key_multi2.swift b/test/decl/protocol/special/coding/Inputs/enum_coding_key_multi2.swift new file mode 100644 index 0000000000000..e56a15e194e02 --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/enum_coding_key_multi2.swift @@ -0,0 +1,16 @@ +func foo() { + let _ = NoRawTypeKey.a.stringValue + let _ = NoRawTypeKey(stringValue: "a") + let _ = NoRawTypeKey.a.intValue + let _ = NoRawTypeKey(intValue: 0) + + let _ = StringKey.a.stringValue + let _ = StringKey(stringValue: "A") + let _ = StringKey.a.intValue + let _ = StringKey(intValue: 0) + + let _ = IntKey.a.stringValue + let _ = IntKey(stringValue: "a") + let _ = IntKey.a.intValue + let _ = IntKey(intValue: 3) +} diff --git a/test/decl/protocol/special/coding/Inputs/struct_codable_simple_multi1.swift b/test/decl/protocol/special/coding/Inputs/struct_codable_simple_multi1.swift new file mode 100644 index 0000000000000..9a72ce03fb1ca --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/struct_codable_simple_multi1.swift @@ -0,0 +1,27 @@ +// RUN: %target-typecheck-verify-swift + +// REQUIRES: objc_interop + +import Foundation + +// Simple structs with all Codable properties should get derived conformance to +// Codable. +struct SimpleStruct : Codable { + var x: Int + var y: Double + static var z: String = "foo" + + // These lines have to be within the SimpleStruct type because CodingKeys + // should be private. + func foo() { + // They should receive a synthesized CodingKeys enum. + let _ = SimpleStruct.CodingKeys.self + + // The enum should have a case for each of the vars. + let _ = SimpleStruct.CodingKeys.x + let _ = SimpleStruct.CodingKeys.y + + // Static vars should not be part of the CodingKeys enum. + let _ = SimpleStruct.CodingKeys.z // expected-error {{type 'SimpleStruct.CodingKeys' has no member 'z'}} + } +} diff --git a/test/decl/protocol/special/coding/Inputs/struct_codable_simple_multi2.swift b/test/decl/protocol/special/coding/Inputs/struct_codable_simple_multi2.swift new file mode 100644 index 0000000000000..e1c762b8d296a --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/struct_codable_simple_multi2.swift @@ -0,0 +1,10 @@ +// These are wrapped in a dummy function to avoid binding a global variable. +func foo() { + // They should receive synthesized init(from:) and an encode(to:). + let _ = SimpleStruct.init(from:) + let _ = SimpleStruct.encode(to:) + + // The synthesized CodingKeys type should not be accessible from outside the + // struct. + let _ = SimpleStruct.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} +} diff --git a/test/decl/protocol/special/coding/class_codable_computed_vars.swift b/test/decl/protocol/special/coding/class_codable_computed_vars.swift new file mode 100644 index 0000000000000..b4d0adf98713c --- /dev/null +++ b/test/decl/protocol/special/coding/class_codable_computed_vars.swift @@ -0,0 +1,43 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// REQUIRES: objc_interop + +import Foundation + +// Classes with computed members should get synthesized conformance to Codable, +// but their lazy and computed members should be skipped as part of the +// synthesis. +class ClassWithComputedMembers : Codable { + var x: Int = 1 + lazy var y: Double = .pi + var z: String { + return "foo" + } + + // These lines have to be within the ClassWithComputedMembers type because + // CodingKeys should be private. + func foo() { + // They should receive a synthesized CodingKeys enum. + let _ = ClassWithComputedMembers.CodingKeys.self + + // The enum should have a case for each of the vars. + let _ = ClassWithComputedMembers.CodingKeys.x + + // Lazy vars should not be part of the CodingKeys enum. + let _ = ClassWithComputedMembers.CodingKeys.y // expected-error {{type 'ClassWithComputedMembers.CodingKeys' has no member 'y'}} + + // Computed vars should not be part of the CodingKeys enum. + let _ = ClassWithComputedMembers.CodingKeys.z // expected-error {{type 'ClassWithComputedMembers.CodingKeys' has no member 'z'}} + } +} + +// These are wrapped in a dummy function to avoid binding a global variable. +func foo() { + // They should receive synthesized init(from:) and an encode(to:). + let _ = ClassWithComputedMembers.init(from:) + let _ = ClassWithComputedMembers.encode(to:) + + // The synthesized CodingKeys type should not be accessible from outside the + // class. + let _ = ClassWithComputedMembers.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} +} diff --git a/test/decl/protocol/special/coding/class_codable_inheritance.swift b/test/decl/protocol/special/coding/class_codable_inheritance.swift new file mode 100644 index 0000000000000..10960ffb6cac5 --- /dev/null +++ b/test/decl/protocol/special/coding/class_codable_inheritance.swift @@ -0,0 +1,59 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// REQUIRES: objc_interop + +import Foundation + +class SimpleClass : Codable { + var x: Int = 1 + var y: Double = .pi + static var z: String = "foo" +} + +// The synthesized CodingKeys type should not be accessible from outside the +// class. +let _ = SimpleClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} + +// Classes which inherit from classes that are codable should synthesize Codable +// conformance as well. +class SimpleChildClass : SimpleClass { + var w: Bool = true + + // NOTE: These tests will fail in the future as Codable classes are updated + // to derive Codable conformance instead of inheriting their + // superclass's. Classes currently inherit their parent's Codable + // conformance and we never get the chance to derive a CodingKeys + // type, nor overridden methods. + + // These lines have to be within the SimpleChildClass type because + // CodingKeys should be private. + func foo() { + // They should receive a synthesized CodingKeys enum. + // NOTE: This expected error will need to be removed in the future. + let _ = SimpleChildClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} + + // The enum should have a case for each of the vars. + // NOTE: This expectedxerror will need to be removed in the future. + let _ = SimpleChildClass.CodingKeys.w // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} + + // Inherited vars should not be part of the CodingKeys enum. + // NOTE: This expected error will need to be updated in the future. + // Should be `type 'SimpleClass.CodingKeys' has no member 'x'` + let _ = SimpleChildClass.CodingKeys.x // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} + + // NOTE: This expected error will need to be updated in the future. + // Should be `type 'SimpleClass.CodingKeys' has no member 'y'` + let _ = SimpleChildClass.CodingKeys.y // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} + } +} + +// These are wrapped in a dummy function to avoid binding a global variable. +func foo() { + // They should receive synthesized init(from:) and an encode(to:). + let _ = SimpleChildClass.init(from:) + let _ = SimpleChildClass.encode(to:) + + // The synthesized CodingKeys type should not be accessible from outside the + // class. + let _ = SimpleChildClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} +} diff --git a/test/decl/protocol/special/coding/class_codable_simple.swift b/test/decl/protocol/special/coding/class_codable_simple.swift new file mode 100644 index 0000000000000..a23a77fe188ba --- /dev/null +++ b/test/decl/protocol/special/coding/class_codable_simple.swift @@ -0,0 +1,38 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// REQUIRES: objc_interop + +import Foundation + +// Simple classes with all Codable properties should get derived conformance to +// Codable. +class SimpleClass : Codable { + var x: Int = 1 + var y: Double = .pi + static var z: String = "foo" + + // These lines have to be within the SimpleClass type because CodingKeys + // should be private. + func foo() { + // They should receive a synthesized CodingKeys enum. + let _ = SimpleClass.CodingKeys.self + + // The enum should have a case for each of the vars. + let _ = SimpleClass.CodingKeys.x + let _ = SimpleClass.CodingKeys.y + + // Static vars should not be part of the CodingKeys enum. + let _ = SimpleClass.CodingKeys.z // expected-error {{type 'SimpleClass.CodingKeys' has no member 'z'}} + } +} + +// These are wrapped in a dummy function to avoid binding a global variable. +func foo() { + // They should receive synthesized init(from:) and an encode(to:). + let _ = SimpleClass.init(from:) + let _ = SimpleClass.encode(to:) + + // The synthesized CodingKeys type should not be accessible from outside the + // class. + let _ = SimpleClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} +} diff --git a/test/decl/protocol/special/coding/class_codable_simple_extension.swift b/test/decl/protocol/special/coding/class_codable_simple_extension.swift new file mode 100644 index 0000000000000..be5f6b091d5d5 --- /dev/null +++ b/test/decl/protocol/special/coding/class_codable_simple_extension.swift @@ -0,0 +1,40 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// REQUIRES: objc_interop + +import Foundation + +// Simple classes where Codable conformance is added in extensions should still +// derive conformance. +class SimpleClass { + var x: Int = 1 + var y: Double = .pi + static var z: String = "foo" + + // These lines have to be within the SimpleClass type because CodingKeys + // should be private. + func foo() { + // They should receive a synthesized CodingKeys enum. + let _ = SimpleClass.CodingKeys.self + + // The enum should have a case for each of the vars. + let _ = SimpleClass.CodingKeys.x + let _ = SimpleClass.CodingKeys.y + + // Static vars should not be part of the CodingKeys enum. + let _ = SimpleClass.CodingKeys.z // expected-error {{type 'SimpleClass.CodingKeys' has no member 'z'}} + } +} + +extension SimpleClass : Codable {} + +// These are wrapped in a dummy function to avoid binding a global variable. +func foo() { + // They should receive synthesized init(from:) and an encode(to:). + let _ = SimpleClass.init(from:) + let _ = SimpleClass.encode(to:) + + // The synthesized CodingKeys type should not be accessible from outside the + // class. + let _ = SimpleClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} +} diff --git a/test/decl/protocol/special/coding/class_codable_simple_multi.swift b/test/decl/protocol/special/coding/class_codable_simple_multi.swift new file mode 100644 index 0000000000000..1c6245e1d41d5 --- /dev/null +++ b/test/decl/protocol/special/coding/class_codable_simple_multi.swift @@ -0,0 +1,2 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown %S/Inputs/class_codable_simple_multi1.swift %S/Inputs/class_codable_simple_multi2.swift +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown %S/Inputs/class_codable_simple_multi2.swift %S/Inputs/class_codable_simple_multi1.swift diff --git a/test/decl/protocol/special/coding/enum_coding_key.swift b/test/decl/protocol/special/coding/enum_coding_key.swift new file mode 100644 index 0000000000000..fcc6f6412169c --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key.swift @@ -0,0 +1,103 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// REQUIRES: objc_interop + +import Foundation + +// Enums with no raw type conforming to CodingKey should get implicit derived +// conformance of methods. +enum NoRawTypeKey : CodingKey { + case a, b, c +} + +let _ = NoRawTypeKey.a.stringValue +let _ = NoRawTypeKey(stringValue: "a") +let _ = NoRawTypeKey.a.intValue +let _ = NoRawTypeKey(intValue: 0) + +// Enums with raw type of String conforming to CodingKey should get implicit +// derived conformance of methods. +enum StringKey : String, CodingKey { + case a = "A", b, c = "Foo" +} + +let _ = StringKey.a.stringValue +let _ = StringKey(stringValue: "A") +let _ = StringKey.a.intValue +let _ = StringKey(intValue: 0) + +// Enums with raw type of Int conforming to CodingKey should get implicit +// derived conformance of methods. +enum IntKey : Int, CodingKey { + case a = 3, b, c = 1 +} + +let _ = IntKey.a.stringValue +let _ = IntKey(stringValue: "a") +let _ = IntKey.a.intValue +let _ = IntKey(intValue: 3) + +// Enums with a different raw value conforming to CodingKey should not get +// implicit derived conformance. +enum Int8Key : Int8, CodingKey { // expected-error {{type 'Int8Key' does not conform to protocol 'CodingKey'}} + case a = -1, b = 0, c = 1 +} + +// Structs conforming to CodingKey should not get implicit derived conformance. +struct StructKey : CodingKey { // expected-error {{type 'StructKey' does not conform to protocol 'CodingKey'}} + // expected-note@-1 {{candidate has non-matching type '()'}} + // expected-note@-2 {{candidate has non-matching type '()'}} +} + +// Classes conforming to CodingKey should not get implict derived conformance. +class ClassKey : CodingKey { //expected-error {{type 'ClassKey' does not conform to protocol 'CodingKey'}} + // expected-note@-1 {{candidate has non-matching type '()'}} + // expected-note@-2 {{candidate has non-matching type '()'}} +} + +// Types which are valid for CodingKey derived conformance should not get that +// derivation unless they explicitly conform to CodingKey. +enum X { case a } +enum Y : String { case a } // expected-note {{did you mean the implicitly-synthesized property 'rawValue'?}} +enum Z : Int { case a } // expected-note {{did you mean the implicitly-synthesized property 'rawValue'?}} + +let _ = X.a.stringValue // expected-error {{value of type 'X' has no member 'stringValue'}} +let _ = Y.a.stringValue // expected-error {{value of type 'Y' has no member 'stringValue'}} +let _ = Z.a.stringValue // expected-error {{value of type 'Z' has no member 'stringValue'}} + +let _ = X(stringValue: "a") // expected-error {{'X' cannot be constructed because it has no accessible initializers}} +let _ = Y(stringValue: "a") // expected-error {{incorrect argument label in call (have 'stringValue:', expected 'rawValue:')}} +let _ = Z(stringValue: "a") // expected-error {{incorrect argument label in call (have 'stringValue:', expected 'rawValue:')}} + +let _ = X.a.intValue // expected-error {{value of type 'X' has no member 'intValue'}} +let _ = Y.a.intValue // expected-error {{value of type 'Y' has no member 'intValue'}} +let _ = Z.a.intValue // expected-error {{value of type 'Z' has no member 'intValue'}} + +let _ = X(intValue: 0) // expected-error {{'X' cannot be constructed because it has no accessible initializers}} +let _ = Y(intValue: 0) // expected-error {{incorrect argument label in call (have 'intValue:', expected 'rawValue:')}} +let _ = Z(intValue: 0) // expected-error {{incorrect argument label in call (have 'intValue:', expected 'rawValue:')}} + +// Types which are valid for CodingKey derived conformance should get derivation +// through extensions. +enum X2 { case a } +enum Y2 : String { case a } +enum Z2 : Int { case a } +extension X2 : CodingKey {} +extension Y2 : CodingKey {} +extension Z2 : CodingKey {} + +let _ = X2.a.stringValue +let _ = Y2.a.stringValue +let _ = Z2.a.stringValue + +let _ = X2(stringValue: "a") +let _ = Y2(stringValue: "a") +let _ = Z2(stringValue: "a") + +let _ = X2.a.intValue +let _ = Y2.a.intValue +let _ = Z2.a.intValue + +let _ = X2(intValue: 0) +let _ = Y2(intValue: 0) +let _ = Z2(intValue: 0) diff --git a/test/decl/protocol/special/coding/enum_coding_key_empty.swift b/test/decl/protocol/special/coding/enum_coding_key_empty.swift new file mode 100644 index 0000000000000..32c9f121bbcce --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_empty.swift @@ -0,0 +1,13 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import Foundation + +// Cannot create an enum with a raw value but no cases. +enum EmptyEnum : CodingKey {} + +// Cannot check accessors since there are no instances of EmptyEnum to test on. +guard EmptyEnum(stringValue: "") == nil else { fatalError() } +guard EmptyEnum(intValue: 0) == nil else { fatalError() } diff --git a/test/decl/protocol/special/coding/enum_coding_key_extension.swift b/test/decl/protocol/special/coding/enum_coding_key_extension.swift new file mode 100644 index 0000000000000..e8179e8bcf84d --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_extension.swift @@ -0,0 +1,40 @@ +// RUN: %target-typecheck-verify-swift + +// REQUIRES: objc_interop + +import Foundation + +// Enums where CodingKey conformance is added in extensions should still derive +// conformance. +enum NoRawTypeKey { + case a, b, c +} + +extension NoRawTypeKey : CodingKey {} + +let _ = NoRawTypeKey.a.stringValue +let _ = NoRawTypeKey(stringValue: "a") +let _ = NoRawTypeKey.a.intValue +let _ = NoRawTypeKey(intValue: 0) + +enum StringKey : String { + case a = "A", b, c = "Foo" +} + +extension StringKey : CodingKey {} + +let _ = StringKey.a.stringValue +let _ = StringKey(stringValue: "A") +let _ = StringKey.a.intValue +let _ = StringKey(intValue: 0) + +enum IntKey : Int { + case a = 3, b, c = 1 +} + +extension IntKey : CodingKey {} + +let _ = IntKey.a.stringValue +let _ = IntKey(stringValue: "a") +let _ = IntKey.a.intValue +let _ = IntKey(intValue: 3) diff --git a/test/decl/protocol/special/coding/enum_coding_key_extension_multi.swift b/test/decl/protocol/special/coding/enum_coding_key_extension_multi.swift new file mode 100644 index 0000000000000..e40d501f4d590 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_extension_multi.swift @@ -0,0 +1,6 @@ +// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/enum_coding_key_extension_multi1.swift %S/Inputs/enum_coding_key_extension_multi2.swift %S/Inputs/enum_coding_key_extension_multi3.swift +// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/enum_coding_key_extension_multi1.swift %S/Inputs/enum_coding_key_extension_multi3.swift %S/Inputs/enum_coding_key_extension_multi2.swift +// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/enum_coding_key_extension_multi2.swift %S/Inputs/enum_coding_key_extension_multi1.swift %S/Inputs/enum_coding_key_extension_multi3.swift +// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/enum_coding_key_extension_multi2.swift %S/Inputs/enum_coding_key_extension_multi3.swift %S/Inputs/enum_coding_key_extension_multi1.swift +// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/enum_coding_key_extension_multi3.swift %S/Inputs/enum_coding_key_extension_multi1.swift %S/Inputs/enum_coding_key_extension_multi2.swift +// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/enum_coding_key_extension_multi3.swift %S/Inputs/enum_coding_key_extension_multi2.swift %S/Inputs/enum_coding_key_extension_multi1.swift diff --git a/test/decl/protocol/special/coding/enum_coding_key_int_raw_type.swift b/test/decl/protocol/special/coding/enum_coding_key_int_raw_type.swift new file mode 100644 index 0000000000000..91c3a334dc7c4 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_int_raw_type.swift @@ -0,0 +1,52 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import Foundation + +enum IntKey : Int, CodingKey { + case a = 3 + case b // Implicitly 4 + case c = 1 +} + +for (val, str, idx) in [(IntKey.a, "a", 3), (.b, "b", 4), (.c, "c", 1)] { + // Keys with a raw type of Int should get a stringValue which matches the + // case name. + guard val.stringValue == str else { fatalError() } + guard IntKey(stringValue: str) == val else { fatalError() } + + // Keys with a raw type of Int should get an intValue based on their + // rawValue. + guard val.intValue == idx else { fatalError() } + guard IntKey(intValue: idx) == val else { fatalError() } +} + +enum PartialIntKey : Int, CodingKey { + case a = 3 + case b // Implicitly 4 + case c = 1 + + var intValue: Int? { + return self.rawValue + 1 + } + + var stringValue: String? { + switch self { + case .a: return "A" + case .b: return "B" + case .c: return "C" + } + } +} + +for (val, str, idx) in [(PartialIntKey.a, "a", 3), (.b, "b", 4), (.c, "c", 1)] { + guard val.stringValue == str.uppercased() else { fatalError() } + guard val.intValue == idx + 1 else { fatalError() } + + // Keys which define some methods should still get derived conformance + // to the others. + guard PartialIntKey(stringValue: str) == val else { fatalError() } + guard PartialIntKey(intValue: idx) == val else { fatalError() } +} diff --git a/test/decl/protocol/special/coding/enum_coding_key_multi.swift b/test/decl/protocol/special/coding/enum_coding_key_multi.swift new file mode 100644 index 0000000000000..620b75341bbb4 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_multi.swift @@ -0,0 +1,2 @@ +// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/enum_coding_key_multi1.swift %S/Inputs/enum_coding_key_multi2.swift +// RUN: %target-swift-frontend -typecheck -verify %S/Inputs/enum_coding_key_multi2.swift %S/Inputs/enum_coding_key_multi1.swift diff --git a/test/decl/protocol/special/coding/enum_coding_key_no_raw_type.swift b/test/decl/protocol/special/coding/enum_coding_key_no_raw_type.swift new file mode 100644 index 0000000000000..d0411b097094f --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_no_raw_type.swift @@ -0,0 +1,51 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import Foundation + +enum ImplicitKey : CodingKey { + case a, b, c +} + +for (index, (val, str)) in [(ImplicitKey.a, "a"), (.b, "b"), (.c, "c")].enumerated() { + // Keys with no raw type should get a stringValue which matches the case + // name. + guard val.stringValue == str else { fatalError() } + guard ImplicitKey(stringValue: str) == val else { fatalError() } + + // They should not have an intValue. + guard val.intValue == nil else { fatalError() } + guard ImplicitKey(intValue: index) == nil else { fatalError() } +} + +enum PartialImplicitKey : CodingKey { + case a, b, c + + var intValue: Int? { + switch self { + case .a: return 1 + case .b: return 2 + case .c: return 3 + } + } + + var stringValue: String? { + switch self { + case .a: return "A" + case .b: return "B" + case .c: return "C" + } + } +} + +for (val, str, idx) in [(PartialImplicitKey.a, "a", 1), (.b, "b", 2), (.c, "c", 3)] { + guard val.stringValue == str.uppercased() else { fatalError() } + guard val.intValue == idx else { fatalError() } + + // Keys which define some methods should still get derived conformance + // to the others. + guard PartialImplicitKey(stringValue: str) == val else { fatalError() } + guard PartialImplicitKey(intValue: idx) == nil else { fatalError() } +} diff --git a/test/decl/protocol/special/coding/enum_coding_key_partial_implementation.swift b/test/decl/protocol/special/coding/enum_coding_key_partial_implementation.swift new file mode 100644 index 0000000000000..7c1ed7277cca7 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_partial_implementation.swift @@ -0,0 +1,151 @@ +// RUN: %target-typecheck-verify-swift + +// REQUIRES: objc_interop + +import Foundation + +// Enums which conform to CodingKey and which should derive conformance should +// get all synthesized methods that they do not implement themselves. +enum NoRawTypeKey1 : CodingKey { + case a, b, c +} + +let _ = NoRawTypeKey1.a.stringValue +let _ = NoRawTypeKey1(stringValue: "a") +let _ = NoRawTypeKey1.a.intValue +let _ = NoRawTypeKey1(intValue: 0) + +enum NoRawTypeKey2 : CodingKey { + case a, b, c + var stringValue: String? { return nil } +} + +let _ = NoRawTypeKey2.a.stringValue +let _ = NoRawTypeKey2(stringValue: "a") +let _ = NoRawTypeKey2.a.intValue +let _ = NoRawTypeKey2(intValue: 0) + +enum NoRawTypeKey3 : CodingKey { + case a, b, c + var intValue: Int? { return nil } +} + +let _ = NoRawTypeKey3.a.stringValue +let _ = NoRawTypeKey3(stringValue: "a") +let _ = NoRawTypeKey3.a.intValue +let _ = NoRawTypeKey3(intValue: 0) + +enum NoRawTypeKey4 : CodingKey { + case a, b, c + init?(stringValue: String) { return nil } +} + +let _ = NoRawTypeKey4.a.stringValue +let _ = NoRawTypeKey4(stringValue: "a") +let _ = NoRawTypeKey4.a.intValue +let _ = NoRawTypeKey4(intValue: 0) + +enum NoRawTypeKey5 : CodingKey { + case a, b, c + init?(intValue: Int) { return nil } +} + +let _ = NoRawTypeKey5.a.stringValue +let _ = NoRawTypeKey5(stringValue: "a") +let _ = NoRawTypeKey5.a.intValue +let _ = NoRawTypeKey5(intValue: 0) + +enum NoRawTypeKey6 : CodingKey { + case a, b, c + var stringValue: String? { return nil } + var intValue: Int? { return nil } +} + +let _ = NoRawTypeKey6.a.stringValue +let _ = NoRawTypeKey6(stringValue: "a") +let _ = NoRawTypeKey6.a.intValue +let _ = NoRawTypeKey6(intValue: 0) + +enum NoRawTypeKey7 : CodingKey { + case a, b, c + var stringValue: String? { return nil } + init?(stringValue: String) { return nil } +} + +let _ = NoRawTypeKey7.a.stringValue +let _ = NoRawTypeKey7(stringValue: "a") +let _ = NoRawTypeKey7.a.intValue +let _ = NoRawTypeKey7(intValue: 0) + +enum NoRawTypeKey8 : CodingKey { + case a, b, c + var stringValue: String? { return nil } + init?(intValue: Int) { return nil } +} + +let _ = NoRawTypeKey8.a.stringValue +let _ = NoRawTypeKey8(stringValue: "a") +let _ = NoRawTypeKey8.a.intValue +let _ = NoRawTypeKey8(intValue: 0) + +enum NoRawTypeKey9 : CodingKey { + case a, b, c + var intValue: Int? { return nil } + init?(stringValue: String) { return nil } +} + +let _ = NoRawTypeKey9.a.stringValue +let _ = NoRawTypeKey9(stringValue: "a") +let _ = NoRawTypeKey9.a.intValue +let _ = NoRawTypeKey9(intValue: 0) + +enum NoRawTypeKey10 : CodingKey { + case a, b, c + var intValue: Int? { return nil } + init?(intValue: Int) { return nil } +} + +let _ = NoRawTypeKey10.a.stringValue +let _ = NoRawTypeKey10(stringValue: "a") +let _ = NoRawTypeKey10.a.intValue +let _ = NoRawTypeKey10(intValue: 0) + +enum NoRawTypeKey11 : CodingKey { + case a, b, c + var stringValue: String? { return nil } + var intValue: Int? { return nil } + init?(stringValue: String) { return nil } +} + +let _ = NoRawTypeKey11.a.stringValue +let _ = NoRawTypeKey11(stringValue: "a") +let _ = NoRawTypeKey11.a.intValue +let _ = NoRawTypeKey11(intValue: 0) + +enum NoRawTypeKey12 : CodingKey { + case a, b, c + var stringValue: String? { return nil } + var intValue: Int? { return nil } + init?(intValue: Int) { return nil } +} + +let _ = NoRawTypeKey12.a.stringValue +let _ = NoRawTypeKey12(stringValue: "a") +let _ = NoRawTypeKey12.a.intValue +let _ = NoRawTypeKey12(intValue: 0) + +// Enums which provide implementations for all CodingKey methods should not +// derive conformance (but derived conformance should not interfere with the +// existing methods). +enum NoRawTypeKey13 : CodingKey { + case a, b, c + var stringValue: String? { return nil } + var intValue: Int? { return nil } + init?(stringValue: String) { return nil } + init?(intValue: Int) { return nil } +} + +let _ = NoRawTypeKey13.a.stringValue +let _ = NoRawTypeKey13(stringValue: "a") +let _ = NoRawTypeKey13.a.intValue +let _ = NoRawTypeKey13(intValue: 0) diff --git a/test/decl/protocol/special/coding/enum_coding_key_string_raw_type.swift b/test/decl/protocol/special/coding/enum_coding_key_string_raw_type.swift new file mode 100644 index 0000000000000..4f156d1f3fce8 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_string_raw_type.swift @@ -0,0 +1,55 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import Foundation + +enum StringKey : String, CodingKey { + case a = "A" + case b // Implicitly "b" + case c = "Foo" +} + +for (index, (val, str)) in [(StringKey.a, "A"), (.b, "b"), (.c, "Foo")].enumerated() { + // Keys with a raw type of String should get a stringValue based on their + // rawValue. + guard val.stringValue == str else { fatalError() } + guard StringKey(stringValue: str) == val else { fatalError() } + + // They should not have an intValue. + guard val.intValue == nil else { fatalError() } + guard StringKey(intValue: index) == nil else { fatalError() } +} + +enum PartialStringKey : String, CodingKey { + case a = "A" + case b // Implicitly "b" + case c = "Foo" + + var intValue: Int? { + switch self { + case .a: return 1 + case .b: return 2 + case .c: return 3 + } + } + + var stringValue: String? { + switch self { + case .a: return "x" + case .b: return "y" + case .c: return "z" + } + } +} + +for (val, realStr, str, idx) in [(PartialStringKey.a, "A", "x", 1), (.b, "b", "y", 2), (.c, "Foo", "z", 3)] { + guard val.stringValue == str else { fatalError() } + guard val.intValue == idx else { fatalError() } + + // Keys which define some methods should still get derived conformance + // to the others. + guard PartialStringKey(stringValue: realStr) == val else { fatalError() } + guard PartialStringKey(intValue: idx) == nil else { fatalError() } +} diff --git a/test/decl/protocol/special/coding/struct_codable_computed_vars.swift b/test/decl/protocol/special/coding/struct_codable_computed_vars.swift new file mode 100644 index 0000000000000..b5b6f6db08a74 --- /dev/null +++ b/test/decl/protocol/special/coding/struct_codable_computed_vars.swift @@ -0,0 +1,40 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// REQUIRES: objc_interop + +import Foundation + +// Structs with computed members should get synthesized conformance to Codable, +// but their lazy and computed members should be skipped as part of the +// synthesis. +struct StructWithComputedMembers : Codable { + var x: Int + lazy var y: Double = Double.pi + var z: String { + return "foo" + } + + // These lines have to be within the StructWithComputedMembers type because + // CodingKeys should be private. + func foo() { + // They should receive a synthesized CodingKeys enum. + let _ = StructWithComputedMembers.CodingKeys.self + + // The enum should have a case for each of the vars. + let _ = StructWithComputedMembers.CodingKeys.x + + // Lazy vars should not be part of the CodingKeys enum. + let _ = StructWithComputedMembers.CodingKeys.y // expected-error {{type 'StructWithComputedMembers.CodingKeys' has no member 'y'}} + + // Computed vars should not be part of the CodingKeys enum. + let _ = StructWithComputedMembers.CodingKeys.z // expected-error {{type 'StructWithComputedMembers.CodingKeys' has no member 'z'}} + } +} + +// They should receive synthesized init(from:) and an encode(to:). +let _ = StructWithComputedMembers.init(from:) +let _ = StructWithComputedMembers.encode(to:) + +// The synthesized CodingKeys type should not be accessible from outside the +// struct. +let _ = StructWithComputedMembers.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} diff --git a/test/decl/protocol/special/coding/struct_codable_simple.swift b/test/decl/protocol/special/coding/struct_codable_simple.swift new file mode 100644 index 0000000000000..83d5fbba2ea52 --- /dev/null +++ b/test/decl/protocol/special/coding/struct_codable_simple.swift @@ -0,0 +1,35 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// REQUIRES: objc_interop + +import Foundation + +// Simple structs with all Codable properties should get derived conformance to +// Codable. +struct SimpleStruct : Codable { + var x: Int + var y: Double + static var z: String = "foo" + + // These lines have to be within the SimpleStruct type because CodingKeys + // should be private. + func foo() { + // They should receive a synthesized CodingKeys enum. + let _ = SimpleStruct.CodingKeys.self + + // The enum should have a case for each of the vars. + let _ = SimpleStruct.CodingKeys.x + let _ = SimpleStruct.CodingKeys.y + + // Static vars should not be part of the CodingKeys enum. + let _ = SimpleStruct.CodingKeys.z // expected-error {{type 'SimpleStruct.CodingKeys' has no member 'z'}} + } +} + +// They should receive synthesized init(from:) and an encode(to:). +let _ = SimpleStruct.init(from:) +let _ = SimpleStruct.encode(to:) + +// The synthesized CodingKeys type should not be accessible from outside the +// struct. +let _ = SimpleStruct.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} diff --git a/test/decl/protocol/special/coding/struct_codable_simple_extension.swift b/test/decl/protocol/special/coding/struct_codable_simple_extension.swift new file mode 100644 index 0000000000000..608460336005e --- /dev/null +++ b/test/decl/protocol/special/coding/struct_codable_simple_extension.swift @@ -0,0 +1,40 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// REQUIRES: objc_interop + +import Foundation + +// Simple structs where Codable conformance is added in extensions should still +// derive conformance. +struct SimpleStruct { + var x: Int + var y: Double + static var z: String = "foo" + + // These lines have to be within the SimpleStruct type because CodingKeys + // should be private. + func foo() { + // They should receive synthesized init(from:) and an encode(to:). + let _ = SimpleStruct.init(from:) + let _ = SimpleStruct.encode(to:) + + // They should receive a synthesized CodingKeys enum. + let _ = SimpleStruct.CodingKeys.self + + // The enum should have a case for each of the vars. + let _ = SimpleStruct.CodingKeys.x + let _ = SimpleStruct.CodingKeys.y + + // Static vars should not be part of the CodingKeys enum. + let _ = SimpleStruct.CodingKeys.z // expected-error {{type 'SimpleStruct.CodingKeys' has no member 'z'}} + } +} + +extension SimpleStruct : Codable {} + +// These are wrapped in a dummy function to avoid binding a global variable. +func foo() { + // The synthesized CodingKeys type should not be accessible from outside the + // struct. + let _ = SimpleStruct.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}} +} diff --git a/test/decl/protocol/special/coding/struct_codable_simple_multi.swift b/test/decl/protocol/special/coding/struct_codable_simple_multi.swift new file mode 100644 index 0000000000000..0f6c47cfa0ae0 --- /dev/null +++ b/test/decl/protocol/special/coding/struct_codable_simple_multi.swift @@ -0,0 +1,2 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown %S/Inputs/struct_codable_simple_multi1.swift %S/Inputs/struct_codable_simple_multi2.swift +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown %S/Inputs/struct_codable_simple_multi2.swift %S/Inputs/struct_codable_simple_multi1.swift From d7651ebb98a864cca9803d6fcae90064047e63bc Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Thu, 16 Mar 2017 16:32:22 -0700 Subject: [PATCH 2/9] =?UTF-8?q?classInheritsCodable=20=E2=86=92=20supercla?= =?UTF-8?q?ssIsCodable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Sema/DerivedConformanceCodable.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 06facc3e89516..24092c896c073 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -13,8 +13,9 @@ using namespace DerivedConformance; // Returns whether the type represented by the given ClassDecl inherits from a // type which is Codable. -static bool classInheritsCodable(TypeChecker &tc, ClassDecl *type) { - if (!type || !type->hasSuperclass()) +static bool superclassIsCodable(TypeChecker &tc, ClassDecl *type) { + assert(type && "Cannot get superclass of null type."); + if (!type->hasSuperclass()) return false; ASTContext &C = tc.Context; @@ -226,7 +227,7 @@ getOrSynthesizeCodingKeysDecl(TypeChecker &tc, Decl *parentDecl, // For classes which inherit from something Codable, we provide case `super` // as the first key (to be used in encoding super). auto classDecl = dyn_cast(type); - if (classInheritsCodable(tc, classDecl)) { + if (superclassIsCodable(tc, classDecl)) { // TODO: Ensure the class doesn't already have or inherit a variable named // "`super`"; otherwise we will generate an invalid enum. In that case, // diagnose and bail. @@ -444,7 +445,7 @@ static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { // Classes which inherit from something Codable should encode super as well. auto classDecl = dyn_cast(typeDecl); - if (classInheritsCodable(tc, classDecl)) { + if (superclassIsCodable(tc, classDecl)) { // Need to generate `try super.encode(to: container.superEncoder())` // superEncoder() @@ -540,7 +541,7 @@ static ValueDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, // This method should be marked as 'override' for classes inheriting Codable // conformance from a parent class. if (auto classDecl = dyn_cast(type)) { - if (classInheritsCodable(tc, classDecl)) { + if (superclassIsCodable(tc, classDecl)) { auto attr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); encodeDecl->getAttrs().add(attr); } @@ -712,7 +713,7 @@ static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { // Classes which inherit from something Codable should decode super as well. auto classDecl = dyn_cast(typeDecl); - if (classInheritsCodable(tc, classDecl)) { + if (superclassIsCodable(tc, classDecl)) { // Need to generate `try super.init(from: container.superDecoder())` // superDecoder() From 888fdedc720732514c730dc3c31a7c6ff8dbde15 Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Mon, 20 Mar 2017 14:39:54 -0700 Subject: [PATCH 3/9] Integrate changes from feedback * Add Swift headers * Avoid else-after-return * Convert documentation to Doxygen comments * Follow correct pointer style * Add missing pointer/reference annotations on auto variables * Use SmallDenseSet instead of DenseSet * Split up large functions (and factor out repeated code where possible) * Avoid casting TypeChecker --- lib/AST/Decl.cpp | 8 +- lib/Sema/DerivedConformanceCodable.cpp | 836 +++++++++++---------- lib/Sema/DerivedConformanceCodingKey.cpp | 884 +++++++++++------------ 3 files changed, 857 insertions(+), 871 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index ab77ae58e47ed..d12416b7dceb3 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2102,11 +2102,11 @@ bool NominalTypeDecl::derivesProtocolConformance(ProtocolDecl *protocol) const { auto nominal = rawType->getAnyNominal(); return nominal == C.getStringDecl() || nominal == C.getIntDecl(); - } else { - // Empty enums are allowed to conform as well. - return enumDecl->getAllElements().empty() || - enumDecl->hasOnlyCasesWithoutAssociatedValues(); } + + // Empty enums are allowed to conform as well. + return enumDecl->getAllElements().empty() || + enumDecl->hasOnlyCasesWithoutAssociatedValues(); } default: diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 24092c896c073..b443bb6417892 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -1,3 +1,20 @@ +//===--- DerivedConformanceCodable.cpp - Derived Codable ------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements explicit derivation of the Foundation Codable protocol +// for a struct or class. +// +//===----------------------------------------------------------------------===// +// #include "TypeChecker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -11,55 +28,73 @@ using namespace swift; using namespace DerivedConformance; -// Returns whether the type represented by the given ClassDecl inherits from a -// type which is Codable. -static bool superclassIsCodable(TypeChecker &tc, ClassDecl *type) { - assert(type && "Cannot get superclass of null type."); +/// Returns whether the type represented by the given ClassDecl inherits from a +/// type which is Codable. +/// +/// \param type The ClassDecl whose superclass to look up. +static bool superclassIsCodable(ClassDecl *type) { if (!type->hasSuperclass()) return false; - ASTContext &C = tc.Context; - auto parentDC = type->getDeclContext(); - auto superType = type->getSuperclass(); - return (bool)tc.conformsToProtocol(superType, - C.getProtocol(KnownProtocolKind::Codable), - parentDC, - ConformanceCheckFlags::Used); + auto &C = type->getASTContext(); + auto *codableProto = C.getProtocol(KnownProtocolKind::Codable); + + auto *superclassDecl = type->getSuperclassDecl(); + auto *superclassModule = superclassDecl->getModuleContext(); + return (bool)superclassModule->lookupConformance(type->getSuperclass(), + codableProto, + C.getLazyResolver()); } -// Looks up a type declaration with the given name in the Foundation module. -// -// Asserts that the Foundation module is loaded, that the given name does not -// refer to more than one type, and that the entity with the given name refers -// to a type instead of a different type of declaration. -static TypeDecl* lookupFoundationTypeDecl(ASTContext &C, Identifier name) { - auto foundationModule = C.getLoadedModule(C.Id_Foundation); +/// Looks up a type declaration with the given name in the Foundation module. +/// +/// Asserts that the Foundation module is loaded, that the given name does not +/// refer to more than one type, and that the entity with the given name refers +/// to a type instead of a different type of declaration. +/// +/// \param C The AST context to perform the lookup in. +/// +/// \param name The name of the type declaration to look up. +static TypeDecl *lookupFoundationTypeDecl(ASTContext &C, Identifier name) { + auto *foundationModule = C.getLoadedModule(C.Id_Foundation); assert(foundationModule && "Foundation module must be loaded."); - SmallVector results; + SmallVector results; foundationModule->lookupMember(results, cast(foundationModule), DeclName(name), Identifier()); assert(results.size() == 1 && "Ambiguous/missing type."); - auto typeDecl = dyn_cast(results[0]); + auto *typeDecl = dyn_cast(results[0]); assert(typeDecl && "Found non-type decl."); return typeDecl; } -// Looks up a type with the given name in the Foundation module. -// -// Asserts that the Foundation module is loaded, that the given name does not -// refer to more than one type, and that the entity with the given name refers -// to a type instead of a different type of declaration. +/// Looks up a type with the given name in the Foundation module. +/// +/// Asserts that the Foundation module is loaded, that the given name does not +/// refer to more than one type, and that the entity with the given name refers +/// to a type instead of a different type of declaration. +/// +/// \param C The AST context to perform the lookup in. +/// +/// \param name The name of the type to look up. static Type lookupFoundationType(ASTContext &C, Identifier name) { return lookupFoundationTypeDecl(C, name)->getDeclaredInterfaceType(); } -// Validates that all the variables declared in the given list of declarations -// conform to the Codable protocol. -// -// Produces a diagnostic on the given typechecker for every var which does not -// conform. Calls a success callback for every var which does conform. +/// Validates that all the variables declared in the given list of declarations +/// conform to the Codable protocol. +/// +/// Produces a diagnostic on the given typechecker for every var which does not +/// conform. Calls a success callback for every var which does conform. +/// +/// \param tc The typechecker to use in validating Codable conformance. +/// +/// \param context The DeclContext the var declarations belong to. +/// +/// \param vars The var range to validate. +/// +/// \param callback A callback to call on every valid var decl. template static bool validateVarsConformToCodable(TypeChecker &tc, DeclContext *context, @@ -87,25 +122,31 @@ validateVarsConformToCodable(TypeChecker &tc, DeclContext *context, // If the var decl didn't validate, it may still not have a type; confirm it // has a type before ensuring the type conforms to Codable. - if (varDecl->hasType() && - tc.conformsToProtocol(varDecl->getType(), codableProto, context, - ConformanceCheckFlags::Used)) { - callback(varDecl); - } else { + if (!varDecl->hasType() || + !tc.conformsToProtocol(varDecl->getType(), codableProto, context, + ConformanceCheckFlags::Used)) { // TODO: We should produce a diagnostic note here explaining that we found // a var not conforming to Codable. allConform = false; + continue; } + + callback(varDecl); } return allConform; } -// Validates the given CodingKeys enum decl by ensuring its cases are a 1-to-1 -// match with the stored vars of the given type. -static bool -validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, - NominalTypeDecl *type) { +/// Validates the given CodingKeys enum decl by ensuring its cases are a 1-to-1 +/// match with the stored vars of the given type. +/// +/// \param tc The typechecker to use in validating Codable conformance. +/// +/// \param codingKeysDecl The CodingKeys enum decl to validate. +/// +/// \param type The nominal type decl to validate the CodingKeys against. +static bool validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, + NominalTypeDecl *type) { // Look through all var decls in the given type. // * Filter out lazy/computed vars (currently already done by // getStoredProperties). @@ -118,7 +159,7 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, // NOTE: If we change the behavior to ignore vars with default values, then we // can further filter out the type names to remove those which // correspond to vars with default values. - llvm::DenseSet names; + llvm::SmallDenseSet names; auto storedProperties = type->getStoredProperties(/*skipInaccessible=*/true); auto validVarCallback = [&names](VarDecl *varDecl) { @@ -147,93 +188,107 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, return names.empty(); } -// Fetches or synthesizes a CodingKeys type nested in the given type. -// -// If the given type declaration has a nested CodingKeys entity, returns it if -// it is a valid NominalTypeDecl (nullptr otherwise). -// If the given type declaration does not have a nested CodingKeys entity, -// synthesizes a new enum based on the Codable members of the given type -// (nullptr if unable to synthesize). -static std::pair -getOrSynthesizeCodingKeysDecl(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *type) { - ASTContext &C = tc.Context; - auto parentDC = cast(parentDecl); - auto codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); - auto typeDC = cast(type); - +/// Returns whether the given type has a valid nested CodingKeys enum. +/// +/// If the type has an invalid CodingKeys entity, produces diagnostics to +/// complain about the error. In this case, the error result will be true -- in +/// the case where we don't have a valid CodingKeys enum and have produced +/// diagnostics here, we don't want to then attempt to synthesize a CodingKeys +/// enum. +/// +/// \param tc The typechecker to use in validating Codable conformance. +/// +/// \param type The type decl whose nested CodingKeys type to validate. +static std::pair +hasValidCodingKeysEnum(TypeChecker &tc, NominalTypeDecl *type) { + auto &C = tc.Context; auto codingKeysDecls = type->lookupDirect(DeclName(C.Id_CodingKeys)); - if (!codingKeysDecls.empty()) { - // Only ill-formed code would produce multiple results for this lookup. - // This would get diagnosed later anyway, so we're free to only look at the - // first result here. - auto result = codingKeysDecls.front(); - - auto typeDecl = dyn_cast(result); - if (!typeDecl) { - // TODO: Produce a diagnostic complaining that the "CodingKeys" entity we - // found is not a type. - return {nullptr, /* attempted to synthesize? */false}; - } - - // Ensure that the type we found conforms to the CodingKey protocol. - auto type = typeDecl->getDeclaredInterfaceType(); - if (!tc.conformsToProtocol(type, codingKeyProto, parentDC, - ConformanceCheckFlags::Used)) { - // TODO: Produce a diagnostic complaining that the "CodingKeys" entity we - // found does not conform to CodingKey. - return {nullptr, /* attempted to synthesize? */false}; - } + if (codingKeysDecls.empty()) + return {/* has type? */ false, /* error? */ false}; + + // Only ill-formed code would produce multiple results for this lookup. + // This would get diagnosed later anyway, so we're free to only look at the + // first result here. + auto result = codingKeysDecls.front(); + + auto *codingKeysTypeDecl = dyn_cast(result); + if (!codingKeysTypeDecl) { + // TODO: Produce a diagnostic complaining that the "CodingKeys" entity we + // found is not a type. + return {/* has type? */ true, /* error? */ true}; + } - // CodingKeys should eventually be a nominal type. If it's a typealias, - // we'll need to follow it. - auto codingKeysDecl = dyn_cast(result); - if (auto typealias = dyn_cast(result)) { - // TODO: Do we have to follow through multiple layers of typealiases - // here? Or will getCanonicalType() do that for us? - auto canType = type->getCanonicalType(); - assert(canType); + // Ensure that the type we found conforms to the CodingKey protocol. + auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); + auto codingKeysType = codingKeysTypeDecl->getDeclaredInterfaceType(); + if (!tc.conformsToProtocol(codingKeysType, codingKeyProto, + type->getDeclContext(), + ConformanceCheckFlags::Used)) { + // TODO: Produce a diagnostic complaining that the "CodingKeys" entity we + // found does not conform to CodingKey. + return {/* has type? */ true, /* error? */ true}; + } - codingKeysDecl = type->getNominalOrBoundGenericNominal(); - } + // CodingKeys should eventually be an enum. If it's a typealias, we'll need to + // follow it. + auto *codingKeysEnum = dyn_cast(result); + if (auto *typealias = dyn_cast(result)) { + // TODO: Do we have to follow through multiple layers of typealiases + // here? Or will getCanonicalType() do that for us? + auto canType = codingKeysType->getCanonicalType(); + assert(canType); - if (!codingKeysDecl) { - // We ended up with something that's not a nominal type? Need to look - // into how this would happen. - // TODO: Produce a diagnostic complaining that we cannot derive Codable - // with a CodingKeys enum that is not a nominal type. - return {nullptr, /* attempted to synthesize? */false}; - } + codingKeysEnum = dyn_cast(codingKeysType->getAnyNominal()); + } - // Didn't synthesize this decl. - return {codingKeysDecl, /* attempted to synthesize? */false}; + if (!codingKeysEnum) { + // TODO: Produce a diagnostic complaining that we cannot derive Codable + // with a non-enum CodingKeys type. + return {/* has type? */ true, /* error? */ true}; } - // The type does not have a CodingKeys enum. We can synthesize one here. + bool valid = validateCodingKeysEnum(tc, codingKeysEnum, type); + return {/* has type? */ true, /* error? */ !valid}; +} + +/// Synthesizes a new CodingKeys enum based on the Codable members of the given +/// type (nullptr if unable to synthesize). +/// +/// If able to synthesize the enum, adds it directly to \c type. +/// +/// \param tc The typechecker to use in validating Codable conformance. +/// +/// \param type The nominal type decl whose nested CodingKeys type to +/// synthesize. +static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, + NominalTypeDecl *type) { + auto &C = tc.Context; + auto *typeDC = cast(type); + // We want to look through all the var declarations of this type to create // enum cases based on those var names. - auto proto = C.getProtocol(KnownProtocolKind::CodingKey); - TypeLoc protoTypeLoc[1] = { TypeLoc::withoutLoc(proto->getDeclaredType()) }; + auto *proto = C.getProtocol(KnownProtocolKind::CodingKey); + TypeLoc protoTypeLoc[1] = {TypeLoc::withoutLoc(proto->getDeclaredType())}; MutableArrayRef inherited = C.AllocateCopy(protoTypeLoc); - auto enumDecl = new (C) EnumDecl(SourceLoc(), C.Id_CodingKeys, SourceLoc(), - inherited, nullptr, typeDC); + auto *enumDecl = new (C) EnumDecl(SourceLoc(), C.Id_CodingKeys, SourceLoc(), + inherited, nullptr, typeDC); enumDecl->setImplicit(); enumDecl->setAccessibility(Accessibility::Private); - auto enumDC = cast(enumDecl); - auto mutableEnumDC = cast(enumDecl); + auto *enumDC = cast(enumDecl); + auto *mutableEnumDC = cast(enumDecl); // For classes which inherit from something Codable, we provide case `super` // as the first key (to be used in encoding super). - auto classDecl = dyn_cast(type); - if (superclassIsCodable(tc, classDecl)) { + auto *classDecl = dyn_cast(type); + if (classDecl && superclassIsCodable(classDecl)) { // TODO: Ensure the class doesn't already have or inherit a variable named // "`super`"; otherwise we will generate an invalid enum. In that case, // diagnose and bail. - auto super = new (C) EnumElementDecl(SourceLoc(), C.Id_super, TypeLoc(), - /*HasArgumentType=*/false, - SourceLoc(), nullptr, enumDC); + auto *super = new (C) EnumElementDecl(SourceLoc(), C.Id_super, TypeLoc(), + /*HasArgumentType=*/false, + SourceLoc(), nullptr, enumDC); super->setImplicit(); mutableEnumDC->addMember(super); } @@ -242,85 +297,107 @@ getOrSynthesizeCodingKeysDecl(TypeChecker &tc, Decl *parentDecl, // conforms to Codable, add it to the enum. auto storedProperties = type->getStoredProperties(/*skipInaccessible=*/true); auto validVarCallback = [&C, &enumDC, &mutableEnumDC](VarDecl *varDecl) { - auto elt = new (C) EnumElementDecl(SourceLoc(), varDecl->getName(), - TypeLoc(), /*HasArgumentType=*/false, - SourceLoc(), nullptr, - enumDC); + auto *elt = new (C) EnumElementDecl(SourceLoc(), varDecl->getName(), + TypeLoc(), /*HasArgumentType=*/false, + SourceLoc(), nullptr, enumDC); elt->setImplicit(); mutableEnumDC->addMember(elt); }; - if (!validateVarsConformToCodable(tc, parentDC, storedProperties, - validVarCallback)) - return {nullptr, /* attempted to synthesize? */true}; + if (!validateVarsConformToCodable(tc, type->getDeclContext(), + storedProperties, validVarCallback)) + return nullptr; // Forcibly derive conformance to CodingKey. tc.checkConformancesInContext(enumDC, mutableEnumDC); - // "Synthesized" a null enum decl. - return {enumDecl, /* attempted to synthesize? */true}; + // Add to the type. + cast(type)->addMember(enumDecl); + return enumDecl; } -// Creates a new var decl representing -// -// let container : containerBase -// -// containerBase is the name of the type to use as the base (either -// KeyedEncodingContainer or KeyedDecodingContainer). -static VarDecl* -createKeyedContainer(ASTContext &C, DeclContext *DC, Identifier containerBase, - Type keyType) { +/// Creates a new var decl representing +/// +/// let container : containerBase +/// +/// containerBase is the name of the type to use as the base (either +/// KeyedEncodingContainer or KeyedDecodingContainer). +/// +/// \param C The AST context to create the decl in. +/// +/// \param DC The DeclContext to create the decl in. +/// +/// \param containerBase The name of the generic type to bind the key type in. +/// +/// \param keyType The key type to bind to the container type. +static VarDecl *createKeyedContainer(ASTContext &C, DeclContext *DC, + Identifier containerBase, Type keyType) { // Look up Keyed*Container - auto keyedContainerDecl = - cast(lookupFoundationTypeDecl(C, containerBase)); + auto *keyedContainerDecl = + cast(lookupFoundationTypeDecl(C, containerBase)); // Bind Keyed*Container to Keyed*Container - Type boundType[1] = { keyType }; + Type boundType[1] = {keyType}; auto containerType = BoundGenericType::get(keyedContainerDecl, Type(), C.AllocateCopy(boundType)); // let container : Keyed*Container - auto containerDecl = new (C) VarDecl(/*IsStatic=*/false, /*IsLet=*/true, - /*IsCaptureList=*/false, SourceLoc(), - C.Id_container, containerType, DC); + auto *containerDecl = new (C) VarDecl(/*IsStatic=*/false, /*IsLet=*/true, + /*IsCaptureList=*/false, SourceLoc(), + C.Id_container, containerType, DC); containerDecl->setImplicit(); containerDecl->setInterfaceType(containerType); return containerDecl; } -static CallExpr* -createContainerKeyedByCall(ASTContext &C, DeclContext *DC, Expr *base, - Type returnType, NominalTypeDecl *param) { +/// Creates a new CallExpr representing +/// +/// base.container(keyedBy: CodingKeys.self) +/// +/// \param C The AST context to create the expression in. +/// +/// \param DC The DeclContext to create any decls in. +/// +/// \param base The base expression to make the call on. +/// +/// \param returnType The return type of the call. +/// +/// \param param The parameter to the call. +static CallExpr *createContainerKeyedByCall(ASTContext &C, DeclContext *DC, + Expr *base, Type returnType, + NominalTypeDecl *param) { // (keyedBy:) - auto keyedByDecl = new (C) ParamDecl(/*IsLet=*/true, SourceLoc(), - SourceLoc(), C.Id_keyedBy, - SourceLoc(), C.Id_keyedBy, - returnType, DC); + auto *keyedByDecl = new (C) ParamDecl(/*IsLet=*/true, SourceLoc(), + SourceLoc(), C.Id_keyedBy, SourceLoc(), + C.Id_keyedBy, returnType, DC); keyedByDecl->setImplicit(); keyedByDecl->setInterfaceType(returnType); // container(keyedBy:) method name - auto paramList = ParameterList::createWithoutLoc(keyedByDecl); + auto *paramList = ParameterList::createWithoutLoc(keyedByDecl); DeclName callName(C, C.Id_container, paramList); // base.container(keyedBy:) expr - auto unboundCall = new (C) UnresolvedDotExpr(base, SourceLoc(), callName, - DeclNameLoc(), - /*Implicit=*/true); + auto *unboundCall = new (C) UnresolvedDotExpr(base, SourceLoc(), callName, + DeclNameLoc(), + /*Implicit=*/true); // CodingKeys.self expr - auto codingKeysExpr = new (C) DeclRefExpr(ConcreteDeclRef(param), - DeclNameLoc(), /*Implicit=*/true); - auto codingKeysMetaTypeExpr = new (C) DotSelfExpr(codingKeysExpr, SourceLoc(), - SourceLoc()); + auto *codingKeysExpr = new (C) DeclRefExpr(ConcreteDeclRef(param), + DeclNameLoc(), /*Implicit=*/true); + auto *codingKeysMetaTypeExpr = new (C) DotSelfExpr(codingKeysExpr, + SourceLoc(), SourceLoc()); // Full bound base.container(keyedBy: CodingKeys.self) call - Expr* args[1] = { codingKeysMetaTypeExpr }; - Identifier argLabels[1] = { C.Id_keyedBy }; + Expr *args[1] = {codingKeysMetaTypeExpr}; + Identifier argLabels[1] = {C.Id_keyedBy}; return CallExpr::createImplicit(C, unboundCall, C.AllocateCopy(args), C.AllocateCopy(argLabels)); } +/// Synthesizes the body for `func encode(to encoder: Encoder) throws`. +/// +/// \param encodeDecl The function decl whose body to synthesize. static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { // struct Foo : Codable { // var x: Int @@ -340,26 +417,18 @@ static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { // } // The enclosing type decl. - auto typeDecl = cast(encodeDecl->getDeclContext()); + auto *typeDecl = cast(encodeDecl->getDeclContext()); - auto funcDC = cast(encodeDecl); - ASTContext &C = funcDC->getASTContext(); - auto &tc = *(TypeChecker *)C.getLazyResolver(); + auto *funcDC = cast(encodeDecl); + auto &C = funcDC->getASTContext(); // We'll want the CodingKeys enum for this type. - NominalTypeDecl *codingKeysDecl; - bool derived; - std::tie(codingKeysDecl, derived) = getOrSynthesizeCodingKeysDecl(tc, - typeDecl, - typeDecl); - + auto *codingKeysDecl = typeDecl->lookupDirect(DeclName(C.Id_CodingKeys))[0]; // We should have bailed already if: // a) The type does not have CodingKeys assert(codingKeysDecl && "Missing CodingKeys decl."); - // b) We had to derive it right here - assert(!derived && "Had to derive CodingKeys type too late."); - // c) The type is not an enum - auto codingKeysEnum = cast(codingKeysDecl); + // b) The type is not an enum + auto *codingKeysEnum = cast(codingKeysDecl); SmallVector statements; @@ -369,13 +438,13 @@ static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { // let container : KeyedEncodingContainer auto codingKeysType = codingKeysEnum->getDeclaredType(); - auto containerDecl = createKeyedContainer(C, funcDC, - C.Id_KeyedEncodingContainer, - codingKeysType); + auto *containerDecl = createKeyedContainer(C, funcDC, + C.Id_KeyedEncodingContainer, + codingKeysType); - auto containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), - DeclNameLoc(), /*Implicit=*/true, - AccessSemantics::DirectToStorage); + auto *containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), + DeclNameLoc(), /*Implicit=*/true, + AccessSemantics::DirectToStorage); auto enumElements = codingKeysEnum->getAllElements(); if (!enumElements.empty()) { @@ -385,112 +454,122 @@ static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { // encoder auto encoderParam = encodeDecl->getParameterList(1)->get(0); - auto encoderExpr = new (C) DeclRefExpr(ConcreteDeclRef(encoderParam), - DeclNameLoc(), /*Implicit=*/true); + auto *encoderExpr = new (C) DeclRefExpr(ConcreteDeclRef(encoderParam), + DeclNameLoc(), /*Implicit=*/true); // Bound encoder.container(keyedBy: CodingKeys.self) call auto containerType = containerDecl->getInterfaceType(); - auto callExpr = createContainerKeyedByCall(C, funcDC, encoderExpr, - containerType, codingKeysEnum); + auto *callExpr = createContainerKeyedByCall(C, funcDC, encoderExpr, + containerType, codingKeysEnum); // Full `let container = encoder.container(keyedBy: CodingKeys.self)` // binding. - auto containerPattern = new (C) NamedPattern(containerDecl, - /*implicit=*/true); - auto bindingDecl = PatternBindingDecl::create(C, SourceLoc(), - StaticSpellingKind::None, - SourceLoc(), containerPattern, - callExpr, funcDC); + auto *containerPattern = new (C) NamedPattern(containerDecl, + /*implicit=*/true); + auto *bindingDecl = PatternBindingDecl::create(C, SourceLoc(), + StaticSpellingKind::None, + SourceLoc(), + containerPattern, callExpr, + funcDC); statements.push_back(bindingDecl); statements.push_back(containerDecl); // Now need to generate `try container.encode(x, forKey: .x)` for all // existing properties. - for (auto elt : enumElements) { + for (auto *elt : enumElements) { // Only ill-formed code would produce multiple results for this lookup. // This would get diagnosed later anyway, so we're free to only look at // the first result here. auto matchingVars = typeDecl->lookupDirect(DeclName(elt->getName())); // self.x - auto selfRef = createSelfDeclRef(encodeDecl); - auto varExpr = new (C) MemberRefExpr(selfRef, SourceLoc(), - ConcreteDeclRef(matchingVars[0]), - DeclNameLoc(), /*Implicit=*/true); + auto *selfRef = createSelfDeclRef(encodeDecl); + auto *varExpr = new (C) MemberRefExpr(selfRef, SourceLoc(), + ConcreteDeclRef(matchingVars[0]), + DeclNameLoc(), /*Implicit=*/true); // CodingKeys.x - auto eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*implicit=*/true); - auto metaTyRef = TypeExpr::createImplicit(codingKeysType, C); - auto keyExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef); + auto *eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*implicit=*/true); + auto *metaTyRef = TypeExpr::createImplicit(codingKeysType, C); + auto *keyExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef); // encode(_:forKey:) SmallVector argNames{Identifier(), C.Id_forKey}; DeclName name(C, C.Id_encode, argNames); - auto encodeCall = new (C) UnresolvedDotExpr(containerExpr, SourceLoc(), - name, DeclNameLoc(), - /*Implicit=*/true); + auto *encodeCall = new (C) UnresolvedDotExpr(containerExpr, SourceLoc(), + name, DeclNameLoc(), + /*Implicit=*/true); // container.encode(self.x, forKey: CodingKeys.x) - Expr* args[2] = { varExpr, keyExpr }; - auto callExpr = CallExpr::createImplicit(C, encodeCall, - C.AllocateCopy(args), - C.AllocateCopy(argNames)); + Expr *args[2] = {varExpr, keyExpr}; + auto *callExpr = CallExpr::createImplicit(C, encodeCall, + C.AllocateCopy(args), + C.AllocateCopy(argNames)); // try container.encode(self.x, forKey: CodingKeys.x) - auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*Implicit=*/true); + auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); statements.push_back(tryExpr); } } // Classes which inherit from something Codable should encode super as well. - auto classDecl = dyn_cast(typeDecl); - if (superclassIsCodable(tc, classDecl)) { + auto *classDecl = dyn_cast(typeDecl); + if (classDecl && superclassIsCodable(classDecl)) { // Need to generate `try super.encode(to: container.superEncoder())` // superEncoder() - auto method = new (C) UnresolvedDeclRefExpr(DeclName(C.Id_superEncoder), - DeclRefKind::Ordinary, - DeclNameLoc()); + auto *method = new (C) UnresolvedDeclRefExpr( + DeclName(C.Id_superEncoder), DeclRefKind::Ordinary, DeclNameLoc()); // container.superEncoder() - auto superEncoderRef = new (C) DotSyntaxCallExpr(containerExpr, SourceLoc(), - method); + auto *superEncoderRef = new (C) DotSyntaxCallExpr(containerExpr, + SourceLoc(), method); // encode(to:) expr - auto encodeDeclRef = new (C) DeclRefExpr(ConcreteDeclRef(encodeDecl), - DeclNameLoc(), /*Implicit=*/true); + auto *encodeDeclRef = new (C) DeclRefExpr(ConcreteDeclRef(encodeDecl), + DeclNameLoc(), /*Implicit=*/true); // super - auto superRef = new (C) SuperRefExpr(encodeDecl->getImplicitSelfDecl(), - SourceLoc(), /*Implicit=*/true); + auto *superRef = new (C) SuperRefExpr(encodeDecl->getImplicitSelfDecl(), + SourceLoc(), /*Implicit=*/true); // super.encode(to:) - auto encodeCall = new (C) DotSyntaxCallExpr(superRef, SourceLoc(), - encodeDeclRef); + auto *encodeCall = new (C) DotSyntaxCallExpr(superRef, SourceLoc(), + encodeDeclRef); // super.encode(to: container.superEncoder()) - Expr* args[1] = { superEncoderRef }; - Identifier argLabels[1] = { C.Id_to }; - auto callExpr = CallExpr::createImplicit(C, encodeCall, - C.AllocateCopy(args), - C.AllocateCopy(argLabels)); + Expr *args[1] = {superEncoderRef}; + Identifier argLabels[1] = {C.Id_to}; + auto *callExpr = CallExpr::createImplicit(C, encodeCall, + C.AllocateCopy(args), + C.AllocateCopy(argLabels)); // try super.encode(to: container.superEncoder()) - auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*Implicit=*/true); + auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); statements.push_back(tryExpr); } - auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(), - /*implicit=*/true); + auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(), + /*implicit=*/true); encodeDecl->setBody(body); } -static ValueDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *type) { - ASTContext &C = tc.Context; - auto typeDC = cast(type); +/// Synthesizes a function declaration for `encode(to: Encoder) throws` with a +/// lazily synthesized body for the given type. +/// +/// Adds the function declaration to the given type before returning it. +/// +/// \param tc The type checker whose AST context to synthesize the decl in. +/// +/// \param parentDecl The parent declaration of the type. +/// +/// \param type The nominal type to synthesize the function for. +static FuncDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, + NominalTypeDecl *type) { + auto &C = tc.Context; + auto *typeDC = cast(type); // Expected type: (Self) -> (Encoder) throws -> () // Constructed as: func type @@ -516,35 +595,31 @@ static ValueDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, auto innerType = FunctionType::get(inputType, returnType, extInfo); // Params: (self [implicit], Encoder) - auto selfDecl = ParamDecl::createSelf(SourceLoc(), typeDC); - auto encoderParam = new (C) ParamDecl(/*isLet=*/true, SourceLoc(), - SourceLoc(), C.Id_to, - SourceLoc(), C.Id_encoder, - encoderType, typeDC); + auto *selfDecl = ParamDecl::createSelf(SourceLoc(), typeDC); + auto *encoderParam = new (C) ParamDecl(/*isLet=*/true, SourceLoc(), + SourceLoc(), C.Id_to, SourceLoc(), + C.Id_encoder, encoderType, typeDC); encoderParam->setInterfaceType(encoderType); - ParameterList *params[] = { - ParameterList::createWithoutLoc(selfDecl), - ParameterList::createWithoutLoc(encoderParam) - }; + ParameterList *params[] = {ParameterList::createWithoutLoc(selfDecl), + ParameterList::createWithoutLoc(encoderParam)}; // Func name: encode(to: Encoder) DeclName name(C, C.Id_encode, params[1]); - auto encodeDecl = FuncDecl::create(C, SourceLoc(), StaticSpellingKind::None, - SourceLoc(), name, SourceLoc(), - /*Throws=*/true, SourceLoc(), - SourceLoc(), nullptr, params, - TypeLoc::withoutLoc(returnType), typeDC); + auto *encodeDecl = FuncDecl::create(C, SourceLoc(), StaticSpellingKind::None, + SourceLoc(), name, SourceLoc(), + /*Throws=*/true, SourceLoc(), SourceLoc(), + nullptr, params, + TypeLoc::withoutLoc(returnType), typeDC); encodeDecl->setImplicit(); encodeDecl->setBodySynthesizer(deriveBodyCodable_encode); // This method should be marked as 'override' for classes inheriting Codable // conformance from a parent class. - if (auto classDecl = dyn_cast(type)) { - if (superclassIsCodable(tc, classDecl)) { - auto attr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); - encodeDecl->getAttrs().add(attr); - } + auto *classDecl = dyn_cast(type); + if (classDecl && superclassIsCodable(classDecl)) { + auto *attr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); + encodeDecl->getAttrs().add(attr); } // Evaluate the type of Self in (Self) -> (Encoder) throws -> (). @@ -552,8 +627,7 @@ static ValueDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, Type interfaceType; if (auto sig = typeDC->getGenericSignatureOfContext()) { // Evaluate the below, but in a generic environment (if Self is generic). - encodeDecl->setGenericEnvironment( - typeDC->getGenericEnvironmentOfContext()); + encodeDecl->setGenericEnvironment(typeDC->getGenericEnvironmentOfContext()); interfaceType = GenericFunctionType::get(sig, selfType, innerType, FunctionType::ExtInfo()); } else { @@ -573,9 +647,11 @@ static ValueDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, cast(type)->addMember(encodeDecl); return encodeDecl; - } +/// Synthesizes the body for `init(from decoder: Decoder) throws`. +/// +/// \param initDecl The function decl whose body to synthesize. static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { // struct Foo : Codable { // var x: Int @@ -595,26 +671,18 @@ static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { // } // The enclosing type decl. - auto typeDecl = cast(initDecl->getDeclContext()); + auto *typeDecl = cast(initDecl->getDeclContext()); - auto funcDC = cast(initDecl); - ASTContext &C = funcDC->getASTContext(); - auto &tc = *(TypeChecker *)C.getLazyResolver(); + auto *funcDC = cast(initDecl); + auto &C = funcDC->getASTContext(); // We'll want the CodingKeys enum for this type. - NominalTypeDecl *codingKeysDecl; - bool derived; - std::tie(codingKeysDecl, derived) = getOrSynthesizeCodingKeysDecl(tc, - typeDecl, - typeDecl); - + auto *codingKeysDecl = typeDecl->lookupDirect(DeclName(C.Id_CodingKeys))[0]; // We should have bailed already if: // a) The type does not have CodingKeys assert(codingKeysDecl && "Missing CodingKeys decl."); - // b) We had to derive it right here - assert(!derived && "Had to derive CodingKeys type too late."); - // c) The type is not an enum - auto codingKeysEnum = cast(codingKeysDecl); + // b) The type is not an enum + auto *codingKeysEnum = cast(codingKeysDecl); // Generate a reference to containerExpr ahead of time in case there are no // properties to encode or decode, but the type is a class which inherits from @@ -622,13 +690,12 @@ static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { // let container : KeyedDecodingContainer auto codingKeysType = codingKeysEnum->getDeclaredType(); - auto containerDecl = createKeyedContainer(C, funcDC, - C.Id_KeyedEncodingContainer, - codingKeysType); + auto *containerDecl = createKeyedContainer( + C, funcDC, C.Id_KeyedEncodingContainer, codingKeysType); - auto containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), - DeclNameLoc(), /*Implicit=*/true, - AccessSemantics::DirectToStorage); + auto *containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), + DeclNameLoc(), /*Implicit=*/true, + AccessSemantics::DirectToStorage); SmallVector statements; auto enumElements = codingKeysEnum->getAllElements(); @@ -639,126 +706,135 @@ static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { // decoder auto decoderParam = initDecl->getParameterList(1)->get(0); - auto decoderExpr = new (C) DeclRefExpr(ConcreteDeclRef(decoderParam), - DeclNameLoc(), /*Implicit=*/true); + auto *decoderExpr = new (C) DeclRefExpr(ConcreteDeclRef(decoderParam), + DeclNameLoc(), /*Implicit=*/true); // Bound decoder.container(keyedBy: CodingKeys.self) call auto containerType = containerDecl->getInterfaceType(); - auto callExpr = createContainerKeyedByCall(C, funcDC, decoderExpr, - containerType, codingKeysEnum); + auto *callExpr = createContainerKeyedByCall(C, funcDC, decoderExpr, + containerType, codingKeysEnum); // try decoder.container(keyedBy: CodingKeys.self) - auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*implicit=*/true); + auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*implicit=*/true); // Full `let container = decoder.container(keyedBy: CodingKeys.self)` // binding. - auto containerPattern = new (C) NamedPattern(containerDecl, - /*implicit=*/true); - auto bindingDecl = PatternBindingDecl::create(C, SourceLoc(), - StaticSpellingKind::None, - SourceLoc(), containerPattern, - tryExpr, funcDC); + auto *containerPattern = new (C) NamedPattern(containerDecl, + /*implicit=*/true); + auto *bindingDecl = PatternBindingDecl::create(C, SourceLoc(), + StaticSpellingKind::None, + SourceLoc(), + containerPattern, tryExpr, + funcDC); statements.push_back(bindingDecl); statements.push_back(containerDecl); // Now need to generate `x = try container.encode(Type.self, forKey: .x)` // for all existing properties. - for (auto elt : enumElements) { + for (auto *elt : enumElements) { // TODO: Don't decode a let var that has a default value. // Only ill-formed code would produce multiple results for this lookup. // This would get diagnosed later anyway, so we're free to only look at // the first result here. auto matchingVars = typeDecl->lookupDirect(DeclName(elt->getName())); - auto varDecl = cast(matchingVars[0]); + auto *varDecl = cast(matchingVars[0]); // Type.self (where Type === type(of: x) auto varType = varDecl->getType(); - auto metaTyRef = TypeExpr::createImplicit(varType, C); - auto typeExpr = new (C) DotSelfExpr(metaTyRef, SourceLoc(), SourceLoc(), - varType); + auto *metaTyRef = TypeExpr::createImplicit(varType, C); + auto *typeExpr = new (C) DotSelfExpr(metaTyRef, SourceLoc(), SourceLoc(), + varType); // CodingKeys.x - auto eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*implicit=*/true); + auto *eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*implicit=*/true); metaTyRef = TypeExpr::createImplicit(codingKeysType, C); - auto keyExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef); + auto *keyExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef); // container.decode(_:forKey:) SmallVector argNames{Identifier(), C.Id_forKey}; DeclName name(C, C.Id_decode, argNames); - auto decodeCall = new (C) UnresolvedDotExpr(containerExpr, SourceLoc(), - name, DeclNameLoc(), - /*Implicit=*/true); + auto *decodeCall = new (C) UnresolvedDotExpr(containerExpr, SourceLoc(), + name, DeclNameLoc(), + /*Implicit=*/true); // container.decode(Type.self, forKey: CodingKeys.x) - Expr* args[2] = { typeExpr, keyExpr }; - auto callExpr = CallExpr::createImplicit(C, decodeCall, - C.AllocateCopy(args), - C.AllocateCopy(argNames)); + Expr *args[2] = {typeExpr, keyExpr}; + auto *callExpr = CallExpr::createImplicit(C, decodeCall, + C.AllocateCopy(args), + C.AllocateCopy(argNames)); // try container.decode(Type.self, forKey: CodingKeys.x) - auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*Implicit=*/true); - - auto selfRef = createSelfDeclRef(initDecl); - auto varExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), - DeclName(varDecl->getName()), - DeclNameLoc(), - /*implicit=*/true); - auto assignExpr = new (C) AssignExpr(varExpr, SourceLoc(), tryExpr, - /*Implicit=*/true); + auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); + + auto *selfRef = createSelfDeclRef(initDecl); + auto *varExpr = new (C) UnresolvedDotExpr( + selfRef, SourceLoc(), DeclName(varDecl->getName()), DeclNameLoc(), + /*implicit=*/true); + auto *assignExpr = new (C) AssignExpr(varExpr, SourceLoc(), tryExpr, + /*Implicit=*/true); statements.push_back(assignExpr); } } // Classes which inherit from something Codable should decode super as well. - auto classDecl = dyn_cast(typeDecl); - if (superclassIsCodable(tc, classDecl)) { + auto *classDecl = dyn_cast(typeDecl); + if (classDecl && superclassIsCodable(classDecl)) { // Need to generate `try super.init(from: container.superDecoder())` // superDecoder() - auto method = new (C) UnresolvedDeclRefExpr(DeclName(C.Id_superDecoder), - DeclRefKind::Ordinary, - DeclNameLoc()); + auto *method = new (C) UnresolvedDeclRefExpr( + DeclName(C.Id_superDecoder), DeclRefKind::Ordinary, DeclNameLoc()); // container.superDecoder() - auto superDecoderRef = new (C) DotSyntaxCallExpr(containerExpr, SourceLoc(), - method); + auto *superDecoderRef = new (C) DotSyntaxCallExpr(containerExpr, + SourceLoc(), method); // init(from:) expr - auto initDeclRef = new (C) DeclRefExpr(ConcreteDeclRef(initDecl), - DeclNameLoc(), /*Implicit=*/true); + auto *initDeclRef = new (C) DeclRefExpr(ConcreteDeclRef(initDecl), + DeclNameLoc(), /*Implicit=*/true); // super - auto superRef = new (C) SuperRefExpr(initDecl->getImplicitSelfDecl(), - SourceLoc(), /*Implicit=*/true); + auto *superRef = new (C) SuperRefExpr(initDecl->getImplicitSelfDecl(), + SourceLoc(), /*Implicit=*/true); // super.init(from:) - auto decodeCall = new (C) DotSyntaxCallExpr(superRef, SourceLoc(), - initDeclRef); + auto *decodeCall = new (C) DotSyntaxCallExpr(superRef, SourceLoc(), + initDeclRef); // super.decode(from: container.superDecoder()) - Expr* args[1] = { superDecoderRef }; - Identifier argLabels[1] = { C.Id_from }; - auto callExpr = CallExpr::createImplicit(C, decodeCall, - C.AllocateCopy(args), - C.AllocateCopy(argLabels)); + Expr *args[1] = {superDecoderRef}; + Identifier argLabels[1] = {C.Id_from}; + auto *callExpr = CallExpr::createImplicit(C, decodeCall, + C.AllocateCopy(args), + C.AllocateCopy(argLabels)); // try super.init(from: container.superDecoder()) - auto tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), - /*Implicit=*/true); + auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(), + /*Implicit=*/true); statements.push_back(tryExpr); } - auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(), - /*implicit=*/true); + auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(), + /*implicit=*/true); initDecl->setBody(body); } +/// Synthesizes a function declaration for `init(from: Decoder) throws` with a +/// lazily synthesized body for the given type. +/// +/// Adds the function declaration to the given type before returning it. +/// +/// \param tc The type checker whose AST context to synthesize the decl in. +/// +/// \param parentDecl The parent declaration of the type. +/// +/// \param type The nominal type to synthesize the function for. static ValueDecl *deriveCodable_init(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *type) { - ASTContext &C = tc.Context; - auto typeDC = cast(type); + auto &C = tc.Context; + auto *typeDC = cast(type); // Expected type: (Self) -> (Decoder) throws -> (Self) // Constructed as: func type @@ -787,34 +863,33 @@ static ValueDecl *deriveCodable_init(TypeChecker &tc, Decl *parentDecl, // Params: (self [implicit], Decoder) // self should be inout if the type is a value type; not inout otherwise. auto inOut = !isa(type); - auto selfDecl = ParamDecl::createSelf(SourceLoc(), typeDC, - /*isStatic=*/false, - /*isInOut=*/inOut); - auto decoderParamDecl = new (C) ParamDecl(/*isLet=*/true, SourceLoc(), - SourceLoc(), C.Id_from, - SourceLoc(), C.Id_decoder, - decoderType, typeDC); + auto *selfDecl = ParamDecl::createSelf(SourceLoc(), typeDC, + /*isStatic=*/false, + /*isInOut=*/inOut); + auto *decoderParamDecl = new (C) ParamDecl(/*isLet=*/true, SourceLoc(), + SourceLoc(), C.Id_from, + SourceLoc(), C.Id_decoder, + decoderType, typeDC); decoderParamDecl->setImplicit(); decoderParamDecl->setInterfaceType(decoderType); - auto paramList = ParameterList::createWithoutLoc(decoderParamDecl); + auto *paramList = ParameterList::createWithoutLoc(decoderParamDecl); // Func name: init(from: Decoder) DeclName name(C, C.Id_init, paramList); - auto initDecl = - new (C) ConstructorDecl(name, SourceLoc(), - /*Failability=*/OTK_None, - /*FailabilityLoc=*/SourceLoc(), - /*Throws=*/true, /*ThrowsLoc=*/SourceLoc(), - selfDecl, paramList, - /*GenericParams=*/nullptr, typeDC); + auto *initDecl = new (C) ConstructorDecl( + name, SourceLoc(), + /*Failability=*/OTK_None, + /*FailabilityLoc=*/SourceLoc(), + /*Throws=*/true, /*ThrowsLoc=*/SourceLoc(), selfDecl, paramList, + /*GenericParams=*/nullptr, typeDC); initDecl->setImplicit(); initDecl->setBodySynthesizer(deriveBodyCodable_init); // This constructor should be marked as `required` for non-final classes. if (isa(type) && type->getAttrs().hasAttribute()) { - auto reqAttr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); + auto *reqAttr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); initDecl->getAttrs().add(reqAttr); } @@ -837,8 +912,8 @@ static ValueDecl *deriveCodable_init(TypeChecker &tc, Decl *parentDecl, initDecl->setInterfaceType(interfaceType); initDecl->setInitializerInterfaceType(initializerType); - initDecl->setAccessibility(std::max(type->getFormalAccess(), - Accessibility::Internal)); + initDecl->setAccessibility( + std::max(type->getFormalAccess(), Accessibility::Internal)); // If the type was not imported, the derived conformance is either from the // type itself or an extension, in which case we will emit the declaration @@ -850,66 +925,34 @@ static ValueDecl *deriveCodable_init(TypeChecker &tc, Decl *parentDecl, return initDecl; } -static bool canSynthesizeCodable(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *type) { - // Fetch or synthesize coding keys for this type. We're going to validate them - // against the vars we encounter on this type. - NominalTypeDecl *codingKeysDecl; - bool derived; - std::tie(codingKeysDecl, derived) = getOrSynthesizeCodingKeysDecl(tc, - parentDecl, - type); - - // We attempted to synthesize coding keys for this type but were unable to, - // because of a type mismatch or otherwise. - if (!codingKeysDecl) { - // We better have "derived" this failure; if we couldn't find a CodingKeys - // type, we should have attempted to synthesize something. - assert(derived && "Unable to fetch CodingKeys declaration for type."); - // TODO: Produce a diagnostic here complaining that we couldn't synthesize a - // a CodingKeys type (potentially carry along the diagnostic produced - // in getOrSynthesize...). +/// Returns whether the given type is valid for synthesizing Codable. +/// +/// Checks to see whether the given type has a valid CodingKeys enum, and if +/// not, attempts to synthesize one for it. +/// +/// \param tc The type checker to use in checking for Codable conformance. +/// +/// \param type The type to validate. +static bool canSynthesizeCodable(TypeChecker &tc, NominalTypeDecl *type) { + // First, look up if the type has a valid CodingKeys enum we can use. + bool hasType, error; + std::tie(hasType, error) = hasValidCodingKeysEnum(tc, type); + + // We found a type, but it wasn't valid. + if (error) return false; - } - auto codingKeysEnum = dyn_cast(codingKeysDecl); - if (!codingKeysEnum) { - // We better not have synthesized a decl that wasn't an EnumDecl. This check - // isn't strictly necessary, but good to sanity check. - assert(!derived && "Somehow synthesized a non-enum CodingKeys type."); - - // We can't synthesize Codable if the type has a CodingKeys struct, class, - // or other unknown type. - // TODO: Produce a diagnostic here complaining that we couldn't synthesize - // the Codable requirement because we cannot read through non-enum - // CodingKeys. - return false; - } - - // We have a CodingKeys enum decl. If the decl wasn't derived, it needs to be - // validated. - if (!derived && !validateCodingKeysEnum(tc, codingKeysEnum, type)) { - // TODO: Produce a diagnostic here complaining that we couldn't synthesize - // the Codable requirement because of a mismatch in the enum - // (possibly carry forward the diagnostic produced in - // validateCodingKeys...). - return false; + // We can try to synthesize a type here. + if (!hasType) { + auto *synthesizedEnum = synthesizeCodingKeysEnum(tc, type); + if (!synthesizedEnum) + return false; } - // If we didn't get the chance to synthesize this enum before (currently the - // case as we don't yet have a hook for always deriving the type if need be, - // i.e. with a fake associatedtype requirement) and synthesized it now, we can - // add it to the type. - // We're only going to add it if we're in the context of actually performing - // the synthesis. - if (derived) - cast(type)->addMember(codingKeysEnum); - return true; } -ValueDecl *DerivedConformance::deriveCodable(TypeChecker &tc, - Decl *parentDecl, +ValueDecl *DerivedConformance::deriveCodable(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *type, ValueDecl *requirement) { // We can only synthesize Codable for structs and classes. @@ -917,7 +960,8 @@ ValueDecl *DerivedConformance::deriveCodable(TypeChecker &tc, return nullptr; // Check other preconditions for synthesized conformance. - bool canSynthesize = canSynthesizeCodable(tc, parentDecl, type); + // This synthesizes a CodingKeys enum if possible. + bool canSynthesize = canSynthesizeCodable(tc, type); diag::RequirementKind reqKind; auto name = requirement->getName(); diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp index 91e59c215695f..803a70561b841 100644 --- a/lib/Sema/DerivedConformanceCodingKey.cpp +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -1,3 +1,20 @@ +//===--- DerivedConformanceCodingKey.cpp - Derived CodingKey --------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements explicit derivation of the Foundation CodingKey +// protocol for an enum. +// +//===----------------------------------------------------------------------===// + #include "TypeChecker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -10,354 +27,117 @@ using namespace swift; using namespace DerivedConformance; -static void setNilReturnBody(AbstractFunctionDecl *funcDecl) { - auto parentDC = funcDecl->getDeclContext(); - ASTContext &C = parentDC->getASTContext(); - - auto nilExpr = new (C) NilLiteralExpr(SourceLoc(), /*Implicit=*/true); - auto returnStmt = new (C) ReturnStmt(SourceLoc(), nilExpr); - auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), - SourceLoc()); +/// Sets the body of the given function to `return nil`. +/// +/// \param funcDecl The function whose body to set. +static void deriveNilReturn(AbstractFunctionDecl *funcDecl) { + auto *parentDC = funcDecl->getDeclContext(); + auto &C = parentDC->getASTContext(); + + auto *nilExpr = new (C) NilLiteralExpr(SourceLoc(), /*Implicit=*/true); + auto *returnStmt = new (C) ReturnStmt(SourceLoc(), nilExpr); + auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), + SourceLoc()); funcDecl->setBody(body); } -static void setRawValueReturnBody(AbstractFunctionDecl *funcDecl) { - auto parentDC = funcDecl->getDeclContext(); - ASTContext &C = parentDC->getASTContext(); - - auto selfRef = createSelfDeclRef(funcDecl); - auto memberRef = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), - C.Id_rawValue, DeclNameLoc(), - /*Implicit=*/true); - - auto returnStmt = new (C) ReturnStmt(SourceLoc(), memberRef); - auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), - SourceLoc()); +/// Sets the body of the given function to `return self.rawValue`. +/// +/// \param funcDecl The function whose body to set. +static void deriveRawValueReturn(AbstractFunctionDecl *funcDecl) { + auto *parentDC = funcDecl->getDeclContext(); + auto &C = parentDC->getASTContext(); + + auto *selfRef = createSelfDeclRef(funcDecl); + auto *memberRef = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), + C.Id_rawValue, DeclNameLoc(), + /*Implicit=*/true); + + auto *returnStmt = new (C) ReturnStmt(SourceLoc(), memberRef); + auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), + SourceLoc()); funcDecl->setBody(body); } -static void -deriveBodyCodingKey_enum_stringValue_raw(AbstractFunctionDecl *strValDecl) { - // enum SomeStringEnum : String { - // case A = "a", B = "b", C = "c" - // @derived var stringValue: String? { - // return self.rawValue - // } - // } - - // Guaranteed to have a rawValue because you cannot declare an enum with a - // raw type but no cases. - setRawValueReturnBody(strValDecl); -} - -static void -deriveBodyCodingKey_enum_stringValue_switch(AbstractFunctionDecl *strValDecl) { - // enum SomeEnum { - // case A, B, C - // @derived var stringValue: String? { - // switch self { - // case A: - // return "A" - // case B: - // return "B" - // case C: - // return "C" - // } - // } - // } - auto parentDC = strValDecl->getDeclContext(); - ASTContext &C = parentDC->getASTContext(); - - auto enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); - Type enumType = parentDC->getDeclaredTypeInContext(); +/// Sets the body of the given function to `self.init(rawValue:)`, passing along +/// the parameter of the given constructor. +/// +/// \param initDecl The constructor whose body to set. +static void deriveRawValueInit(AbstractFunctionDecl *initDecl) { + auto *parentDC = initDecl->getDeclContext(); + auto &C = parentDC->getASTContext(); - auto elements = enumDecl->getAllElements(); - if (elements.empty() /* empty enum */) { - setNilReturnBody(strValDecl); - return; - } - - SmallVector cases; - for (auto elt : elements) { - auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, nullptr); - pat->setImplicit(); - - auto labelItem = - CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), nullptr); - - auto returnExpr = new (C) StringLiteralExpr(elt->getNameStr(), - SourceRange(), - /*Implicit=*/true); - auto returnStmt = new (C) ReturnStmt(SourceLoc(), returnExpr); - auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), - SourceLoc()); - cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, - /*HasBoundDecls=*/false, SourceLoc(), - body)); - } - - auto selfRef = createSelfDeclRef(strValDecl); - auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), selfRef, - SourceLoc(), cases, SourceLoc(), C); - auto body = BraceStmt::create(C, SourceLoc(), ASTNode(switchStmt), - SourceLoc()); - strValDecl->setBody(body); -} - -static ValueDecl * -deriveCodingKey_enum_stringValue(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl) { - ASTContext &C = tc.Context; - auto parentDC = cast(parentDecl); - auto optionalStringType = OptionalType::get(OTK_Optional, - tc.getStringType(parentDC)); - - // Define the getter. - auto getterDecl = declareDerivedPropertyGetter(tc, parentDecl, enumDecl, - optionalStringType, - optionalStringType, - /*isStatic=*/false, - /*isFinal=*/false); - - auto rawType = enumDecl->getRawType(); - if (rawType && rawType->isEqual(tc.getStringType(parentDC))) { - getterDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_stringValue_raw); - } else { - getterDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_stringValue_switch); - } - - // Define the property. - VarDecl *propDecl; - PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) - = declareDerivedReadOnlyProperty(tc, parentDecl, enumDecl, - C.Id_stringValue, - optionalStringType, - optionalStringType, - getterDecl, - /*isStatic=*/false, - /*isFinal=*/false); - - auto dc = cast(parentDecl); - dc->addMember(getterDecl); - dc->addMember(propDecl); - dc->addMember(pbDecl); - return propDecl; -} - -static void -deriveBodyCodingKey_enum_intValue_raw(AbstractFunctionDecl *intValDecl) { - // enum SomeIntEnum : Int { - // case A = 1, B = 2, C = 3 - // @derived var intValue: Int? { - // return self.rawValue - // } - // } - - // Guaranteed to have a rawValue because you cannot declare an enum with a - // raw type but no cases. - setRawValueReturnBody(intValDecl); -} - -void deriveBodyCodingKey_enum_intValue_nil(AbstractFunctionDecl *intValDecl) { - // enum SomeEnum { - // case A, B, C - // @derived var intValue: Int? { - // return nil - // } - // } - setNilReturnBody(intValDecl); -} - -static ValueDecl * -deriveCodingKey_enum_intValue(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl) { - ASTContext &C = tc.Context; - auto parentDC = cast(parentDecl); - auto optionalIntType = OptionalType::get(OTK_Optional, - tc.getIntType(parentDC)); - - // Define the getter. - auto getterDecl = declareDerivedPropertyGetter(tc, parentDecl, enumDecl, - optionalIntType, - optionalIntType, - /*isStatic=*/false, - /*isFinal=*/false); - - auto rawType = enumDecl->getRawType(); - if (rawType && rawType->isEqual(tc.getIntType(parentDC))) { - getterDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_intValue_raw); - } else { - getterDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_intValue_nil); - } - - // Define the property. - VarDecl *propDecl; - PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) - = declareDerivedReadOnlyProperty(tc, parentDecl, enumDecl, - C.Id_intValue, - optionalIntType, - optionalIntType, - getterDecl, - /*isStatic=*/false, - /*isFinal=*/false); - - auto dc = cast(parentDecl); - dc->addMember(getterDecl); - dc->addMember(propDecl); - dc->addMember(pbDecl); - return propDecl; -} - -static void -deriveBodyCodingKey_enum_init_stringValue_raw(AbstractFunctionDecl *initDecl) { - // enum SomeStringEnum : String { - // case A = "a", B = "b", C = "c" - // @derived init?(stringValue: String) { - // self.init(rawValue: stringValue) - // } - // } - auto parentDC = initDecl->getDeclContext(); - ASTContext &C = parentDC->getASTContext(); + // Get the param from init({string,int}Value:). self is the first param in the + // list; stringValue is the second. + auto *valueParam = initDecl->getParameterList(1)->get(0); + auto *valueParamExpr = new (C) DeclRefExpr(ConcreteDeclRef(valueParam), + DeclNameLoc(), /*Implicit=*/true); // rawValue param to init(rawValue:) - auto rawValueDecl = new (C) ParamDecl(/*IsLet=*/true, SourceLoc(), - SourceLoc(), C.Id_rawValue, - SourceLoc(), C.Id_rawValue, - C.getStringDecl()->getDeclaredType(), - parentDC); + auto *rawValueDecl = new (C) ParamDecl(/*IsLet=*/true, SourceLoc(), + SourceLoc(), C.Id_rawValue, + SourceLoc(), C.Id_rawValue, + valueParam->getType(), parentDC); rawValueDecl->setInterfaceType(C.getIntDecl()->getDeclaredType()); rawValueDecl->setImplicit(); - auto paramList = ParameterList::createWithoutLoc(rawValueDecl); + auto *paramList = ParameterList::createWithoutLoc(rawValueDecl); // init(rawValue:) constructor name DeclName ctorName(C, C.Id_init, paramList); - auto selfRef = createSelfDeclRef(initDecl); - // self.init(rawValue:) expr - auto initExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), - ctorName, DeclNameLoc(), - /*Implicit=*/true); - - // Get the stringValue param from init(stringValue:). self is the first param - // in the list; stringValue is the second. - auto stringValueParam = initDecl->getParameterList(1)->get(0); - auto stringValueExpr = new (C) DeclRefExpr(ConcreteDeclRef(stringValueParam), - DeclNameLoc(), - /*Implicit=*/true); - - // Bind the stringValue param in self.init(rawValue: stringValue). - ArrayRef args{stringValueExpr}; - ArrayRef argLabels{C.Id_rawValue}; - auto callExpr = CallExpr::createImplicit(C, initExpr, args, argLabels); - - auto body = BraceStmt::create(C, SourceLoc(), ASTNode(callExpr), SourceLoc()); + auto *selfRef = createSelfDeclRef(initDecl); + auto *initExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), ctorName, + DeclNameLoc(), /*Implicit=*/true); + + // Bind the value param in self.init(rawValue: {string,int}Value). + Expr *args[1] = {valueParamExpr}; + Identifier argLabels[1] = {C.Id_rawValue}; + auto *callExpr = CallExpr::createImplicit(C, initExpr, C.AllocateCopy(args), + C.AllocateCopy(argLabels)); + + auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(callExpr), + SourceLoc()); initDecl->setBody(body); } -static void -deriveBodyCodingKey_enum_init_stringValue_switch(AbstractFunctionDecl * - initDecl) { - // enum SomeEnum { - // case A, B, C - // @derived init?(stringValue: String) { - // switch stringValue { - // case "A": - // self = .A - // case "B": - // self = .B - // case "C": - // self = .C - // default: - // return nil - // } - // } - // } - auto parentDC = initDecl->getDeclContext(); - ASTContext &C = parentDC->getASTContext(); - - auto enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); - Type enumType = parentDC->getDeclaredTypeInContext(); - - auto elements = enumDecl->getAllElements(); - if (elements.empty() /* empty enum */) { - setNilReturnBody(initDecl); - return; - } - - auto selfRef = createSelfDeclRef(initDecl); - SmallVector cases; - for (auto elt : elements) { - auto litExpr = new (C) StringLiteralExpr(elt->getNameStr(), - SourceRange(), - /*Implicit=*/true); - auto litPat = new (C) ExprPattern(litExpr, /*IsResolved=*/true, - nullptr, nullptr); - litPat->setImplicit(); - - auto labelItem = - CaseLabelItem(/*IsDefault=*/false, litPat, SourceLoc(), nullptr); - - auto eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*Implicit=*/true); - auto metaTyRef = TypeExpr::createImplicit(enumType, C); - auto valueExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef); - - auto assignment = new (C) AssignExpr(selfRef, SourceLoc(), valueExpr, - /*Implicit=*/true); - - auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignment), - SourceLoc()); - cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, - /*HasBoundDecls=*/false, SourceLoc(), - body)); - } - - auto anyPat = new (C) AnyPattern(SourceLoc()); - anyPat->setImplicit(); - auto dfltLabelItem = - CaseLabelItem(/*IsDefault=*/true, anyPat, SourceLoc(), nullptr); - - auto dfltReturnStmt = new (C) FailStmt(SourceLoc(), SourceLoc()); - auto dfltBody = BraceStmt::create(C, SourceLoc(), ASTNode(dfltReturnStmt), - SourceLoc()); - cases.push_back(CaseStmt::create(C, SourceLoc(), dfltLabelItem, - /*HasBoundDecls=*/false, SourceLoc(), - dfltBody)); - - auto stringValueDecl = initDecl->getParameterList(1)->get(0); - auto stringValueRef = new (C) DeclRefExpr(stringValueDecl, DeclNameLoc(), - /*Implicit=*/true); - auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), - stringValueRef, SourceLoc(), cases, - SourceLoc(), C); - auto body = BraceStmt::create(C, SourceLoc(), ASTNode(switchStmt), - SourceLoc()); - initDecl->setBody(body); -} +/// Synthesizes a constructor declaration with the given parameter name and +/// type. +/// +/// \param tc The type checker to use in synthesizing the constructor. +/// +/// \param parentDecl The parent declaration of the enum. +/// +/// \param enumDecl The enum on which to synthesize the constructor. +/// +/// \param paramType The type of the parameter. +/// +/// \param paramName The name of the parameter. +/// +/// \param synthesizer A lambda to call to set the constructor's body. +template +static ValueDecl *deriveInitDecl(TypeChecker &tc, Decl *parentDecl, + EnumDecl *enumDecl, Type paramType, + Identifier paramName, + const Synthesizer &synthesizer) { + auto &C = tc.Context; + auto *parentDC = cast(parentDecl); + + // rawValue + auto *rawDecl = new (C) ParamDecl(/*IsLet*/ true, SourceLoc(), SourceLoc(), + paramName, SourceLoc(), paramName, + paramType, parentDC); + rawDecl->setInterfaceType(paramType); + rawDecl->setImplicit(); -static ValueDecl * -deriveCodingKey_enum_init_stringValue(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl) { - ASTContext &C = tc.Context; - auto parentDC = cast(parentDecl); - auto stringType = tc.getStringType(parentDC); + // init(rawValue:) name + auto *paramList = ParameterList::createWithoutLoc(rawDecl); + DeclName name(C, C.Id_init, paramList); + // init(rawValue:) decl auto *selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC, /*static*/false, /*inout*/true); - - auto *rawDecl = new (C) ParamDecl(/*IsLet*/true, SourceLoc(), SourceLoc(), - C.Id_stringValue, SourceLoc(), - C.Id_stringValue, stringType, parentDC); - rawDecl->setInterfaceType(stringType); - rawDecl->setImplicit(); - auto paramList = ParameterList::createWithoutLoc(rawDecl); - DeclName name(C, C.Id_init, paramList); - - auto initDecl = + auto *initDecl = new (C) ConstructorDecl(name, SourceLoc(), /*Failability=*/OTK_Optional, /*FailabilityLoc=*/SourceLoc(), @@ -367,25 +147,21 @@ deriveCodingKey_enum_init_stringValue(TypeChecker &tc, Decl *parentDecl, initDecl->setImplicit(); - auto rawType = enumDecl->getRawType(); - if (rawType && rawType->isEqual(stringType)) { - initDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_init_stringValue_raw); - } else { - initDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_init_stringValue_switch); - } + // Synthesize the body. + synthesizer(initDecl); // Compute the type of the initializer. - TupleTypeElt element(stringType, C.Id_stringValue); - TupleTypeElt interfaceElement(stringType, C.Id_stringValue); + TupleTypeElt element(paramType, paramName); + TupleTypeElt interfaceElement(paramType, paramName); auto interfaceArgType = TupleType::get(interfaceElement, C); // Compute the interface type of the initializer. - Type retInterfaceType - = OptionalType::get(parentDC->getDeclaredInterfaceType()); + Type retInterfaceType = + OptionalType::get(parentDC->getDeclaredInterfaceType()); Type interfaceType = FunctionType::get(interfaceArgType, retInterfaceType); Type selfInterfaceType = initDecl->computeInterfaceSelfType(); - Type selfInitializerInterfaceType - = initDecl->computeInterfaceSelfType(/*init*/ true); + Type selfInitializerInterfaceType = + initDecl->computeInterfaceSelfType(/*init*/ true); Type allocIfaceType; Type initIfaceType; @@ -419,154 +195,206 @@ deriveCodingKey_enum_init_stringValue(TypeChecker &tc, Decl *parentDecl, return initDecl; } -static void deriveBodyCodingKey_enum_init_intValue_raw(AbstractFunctionDecl * - initDecl) { - // enum SomeIntEnum : Int { - // case A = 1, B = 2, C = 3 - // @derived init?(intValue: Int) { - // self.init(rawValue: intValue) +/// Synthesizes a read-only computed property with a given type and name. +/// +/// \param tc The type checker to use in synthesizing the property. +/// +/// \param parentDecl The parent declaration of the enum. +/// +/// \param enumDecl The enum on which to synthesize the property. +/// +/// \param type The type of the property. +/// +/// \param name The name of the property. +/// +/// \param synthesizer A lambda to call to set the property's getter. +template +static ValueDecl *deriveProperty(TypeChecker &tc, Decl *parentDecl, + EnumDecl *enumDecl, Type type, Identifier name, + const Synthesizer &synthesizer) { + // Define the getter. + auto *getterDecl = declareDerivedPropertyGetter(tc, parentDecl, enumDecl, + type, type, + /*isStatic=*/false, + /*isFinal=*/false); + + // Synthesize the body. + synthesizer(getterDecl); + + // Define the property. + VarDecl *propDecl; + PatternBindingDecl *pbDecl; + std::tie(propDecl, pbDecl) + = declareDerivedReadOnlyProperty(tc, parentDecl, enumDecl, name, type, type, + getterDecl, /*isStatic=*/false, + /*isFinal=*/false); + + auto *dc = cast(parentDecl); + dc->addMember(getterDecl); + dc->addMember(propDecl); + dc->addMember(pbDecl); + return propDecl; +} + +/// Sets the body of the given function to return a string value based on +/// switching on `self`. +/// +/// \param strValDecl The function whose body to set. +static void +deriveBodyCodingKey_enum_stringValue(AbstractFunctionDecl *strValDecl) { + // enum SomeEnum { + // case A, B, C + // @derived var stringValue: String? { + // switch self { + // case A: + // return "A" + // case B: + // return "B" + // case C: + // return "C" + // } // } // } - auto parentDC = initDecl->getDeclContext(); - ASTContext &C = parentDC->getASTContext(); + auto *parentDC = strValDecl->getDeclContext(); + auto &C = parentDC->getASTContext(); - // rawValue param to init(rawValue:) - auto rawValueDecl = new (C) ParamDecl(/*IsLet=*/true, SourceLoc(), - SourceLoc(), C.Id_rawValue, - SourceLoc(), C.Id_rawValue, - C.getIntDecl()->getDeclaredType(), - parentDC); - rawValueDecl->setInterfaceType(C.getIntDecl()->getDeclaredType()); - rawValueDecl->setImplicit(); - auto paramList = ParameterList::createWithoutLoc(rawValueDecl); + auto *enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); + Type enumType = parentDC->getDeclaredTypeInContext(); - // init(rawValue:) constructor name - DeclName ctorName(C, C.Id_init, paramList); + auto elements = enumDecl->getAllElements(); + if (elements.empty() /* empty enum */) { + deriveNilReturn(strValDecl); + return; + } - auto selfRef = createSelfDeclRef(initDecl); + SmallVector cases; + for (auto *elt : elements) { + auto *pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), + SourceLoc(), SourceLoc(), + Identifier(), elt, nullptr); + pat->setImplicit(); - // self.init(rawValue:) expr - auto initExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), - ctorName, DeclNameLoc(), - /*Implicit=*/true); - - // Get the intValue param from init(intValue:). self is the first param - // in the list; intValue is the second. - auto intValueParam = initDecl->getParameterList(1)->get(0); - auto intValueExpr = new (C) DeclRefExpr(ConcreteDeclRef(intValueParam), - DeclNameLoc(), - /*Implicit=*/true); + auto labelItem = CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), + nullptr); - // Bind the intValue param in self.init(rawValue: intValue). - ArrayRef args{intValueExpr}; - ArrayRef argLabels{C.Id_rawValue}; - auto callExpr = CallExpr::createImplicit(C, initExpr, args, argLabels); + auto *returnExpr = new (C) StringLiteralExpr(elt->getNameStr(), + SourceRange(), + /*Implicit=*/true); + auto *returnStmt = new (C) ReturnStmt(SourceLoc(), returnExpr); + auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), + SourceLoc()); + cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, + /*HasBoundDecls=*/false, SourceLoc(), + body)); + } - auto body = BraceStmt::create(C, SourceLoc(), ASTNode(callExpr), SourceLoc()); - initDecl->setBody(body); + auto *selfRef = createSelfDeclRef(strValDecl); + auto *switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), selfRef, + SourceLoc(), cases, SourceLoc(), C); + auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(switchStmt), + SourceLoc()); + strValDecl->setBody(body); } +/// Sets the body of the given constructor to initialize `self` based on the +/// value of the given string param. +/// +/// \param initDecl The function whose body to set. static void -deriveBodyCodingKey_enum_init_intValue_nil(AbstractFunctionDecl *initDecl) { +deriveBodyCodingKey_init_stringValue(AbstractFunctionDecl *initDecl) { // enum SomeEnum { // case A, B, C - // @derived init?(intValue: Int) { - // return nil + // @derived init?(stringValue: String) { + // switch stringValue { + // case "A": + // self = .A + // case "B": + // self = .B + // case "C": + // self = .C + // default: + // return nil + // } // } // } - setNilReturnBody(initDecl); -} + auto *parentDC = initDecl->getDeclContext(); + auto &C = parentDC->getASTContext(); -static ValueDecl * -deriveCodingKey_enum_init_intValue(TypeChecker &tc, Decl *parentDecl, - EnumDecl *enumDecl) { - ASTContext &C = tc.Context; - auto parentDC = cast(parentDecl); - auto intType = tc.getIntType(parentDC); - - auto *selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC, - /*static*/false, /*inout*/true); - - auto *rawDecl = new (C) ParamDecl(/*IsLet*/true, SourceLoc(), SourceLoc(), - C.Id_intValue, SourceLoc(), - C.Id_intValue, intType, parentDC); - rawDecl->setInterfaceType(intType); - rawDecl->setImplicit(); - auto paramList = ParameterList::createWithoutLoc(rawDecl); - DeclName name(C, C.Id_init, paramList); - - auto initDecl = - new (C) ConstructorDecl(name, SourceLoc(), - /*Failability=*/OTK_Optional, - /*FailabilityLoc=*/SourceLoc(), - /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), - selfDecl, paramList, - /*GenericParams=*/nullptr, parentDC); - - initDecl->setImplicit(); + auto *enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); + Type enumType = parentDC->getDeclaredTypeInContext(); - auto rawType = enumDecl->getRawType(); - if (rawType && rawType->isEqual(intType)) { - initDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_init_intValue_raw); - } else { - initDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_init_intValue_nil); + auto elements = enumDecl->getAllElements(); + if (elements.empty() /* empty enum */) { + deriveNilReturn(initDecl); + return; } - // Compute the type of the initializer. - TupleTypeElt element(intType, C.Id_intValue); - TupleTypeElt interfaceElement(intType, C.Id_intValue); - auto interfaceArgType = TupleType::get(interfaceElement, C); + auto *selfRef = createSelfDeclRef(initDecl); + SmallVector cases; + for (auto *elt : elements) { + auto *litExpr = new (C) StringLiteralExpr(elt->getNameStr(), SourceRange(), + /*Implicit=*/true); + auto *litPat = new (C) ExprPattern(litExpr, /*IsResolved=*/true, nullptr, + nullptr); + litPat->setImplicit(); - // Compute the interface type of the initializer. - Type retInterfaceType - = OptionalType::get(parentDC->getDeclaredInterfaceType()); - Type interfaceType = FunctionType::get(interfaceArgType, retInterfaceType); - Type selfInterfaceType = initDecl->computeInterfaceSelfType(); - Type selfInitializerInterfaceType - = initDecl->computeInterfaceSelfType(/*init*/ true); + auto labelItem = CaseLabelItem(/*IsDefault=*/false, litPat, SourceLoc(), + nullptr); - Type allocIfaceType; - Type initIfaceType; - if (auto sig = parentDC->getGenericSignatureOfContext()) { - initDecl->setGenericEnvironment(parentDC->getGenericEnvironmentOfContext()); + auto *eltRef = new (C) DeclRefExpr(elt, DeclNameLoc(), /*Implicit=*/true); + auto *metaTyRef = TypeExpr::createImplicit(enumType, C); + auto *valueExpr = new (C) DotSyntaxCallExpr(eltRef, SourceLoc(), metaTyRef); - allocIfaceType = GenericFunctionType::get(sig, selfInterfaceType, - interfaceType, - FunctionType::ExtInfo()); - initIfaceType = GenericFunctionType::get(sig, selfInitializerInterfaceType, - interfaceType, - FunctionType::ExtInfo()); - } else { - allocIfaceType = FunctionType::get(selfInterfaceType, - interfaceType); - initIfaceType = FunctionType::get(selfInitializerInterfaceType, - interfaceType); + auto *assignment = new (C) AssignExpr(selfRef, SourceLoc(), valueExpr, + /*Implicit=*/true); + + auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(assignment), + SourceLoc()); + cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, + /*HasBoundDecls=*/false, SourceLoc(), + body)); } - initDecl->setInterfaceType(allocIfaceType); - initDecl->setInitializerInterfaceType(initIfaceType); - initDecl->setAccessibility(std::max(Accessibility::Internal, - enumDecl->getFormalAccess())); - // If the enum was not imported, the derived conformance is either from the - // enum itself or an extension, in which case we will emit the declaration - // normally. - if (enumDecl->hasClangNode()) - tc.Context.addExternalDecl(initDecl); + auto *anyPat = new (C) AnyPattern(SourceLoc()); + anyPat->setImplicit(); + auto dfltLabelItem = CaseLabelItem(/*IsDefault=*/true, anyPat, SourceLoc(), + nullptr); - cast(parentDecl)->addMember(initDecl); - return initDecl; + auto *dfltReturnStmt = new (C) FailStmt(SourceLoc(), SourceLoc()); + auto *dfltBody = BraceStmt::create(C, SourceLoc(), ASTNode(dfltReturnStmt), + SourceLoc()); + cases.push_back(CaseStmt::create(C, SourceLoc(), dfltLabelItem, + /*HasBoundDecls=*/false, SourceLoc(), + dfltBody)); + + auto *stringValueDecl = initDecl->getParameterList(1)->get(0); + auto *stringValueRef = new (C) DeclRefExpr(stringValueDecl, DeclNameLoc(), + /*Implicit=*/true); + auto *switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), + stringValueRef, SourceLoc(), cases, + SourceLoc(), C); + auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(switchStmt), + SourceLoc()); + initDecl->setBody(body); } -static bool -canSynthesizeCodingKey(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) { +/// Returns whether the given enum is eligible for CodingKey synthesis. +/// +/// \param tc The type checker to use in checking eligibility. +/// +/// \param parentDecl The parent declaration of the enum. +/// +/// \param enumDecl The enum to check. +static bool canSynthesizeCodingKey(TypeChecker &tc, Decl *parentDecl, + EnumDecl *enumDecl) { // If the enum has a raw type (optional), it must be String or Int. Type rawType = enumDecl->getRawType(); if (rawType) { - auto parentDC = cast(parentDecl); + auto *parentDC = cast(parentDecl); rawType = parentDC->mapTypeIntoContext(rawType); - ASTContext &C = tc.Context; - auto nominal = rawType->getCanonicalType()->getAnyNominal(); + auto &C = tc.Context; + auto *nominal = rawType->getCanonicalType()->getAnyNominal(); if (nominal != C.getStringDecl() && nominal != C.getIntDecl()) return false; } @@ -586,7 +414,7 @@ ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, ValueDecl *requirement) { // We can only synthesize CodingKey for enums. - auto enumDecl = dyn_cast(type); + auto *enumDecl = dyn_cast(type); if (!enumDecl) return nullptr; @@ -594,22 +422,136 @@ ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, if (!canSynthesizeCodingKey(tc, parentDecl, enumDecl)) return nullptr; - auto name = requirement->getName(); - if (name == tc.Context.Id_stringValue) { - return deriveCodingKey_enum_stringValue(tc, parentDecl, enumDecl); - } else if (name == tc.Context.Id_intValue) { - return deriveCodingKey_enum_intValue(tc, parentDecl, enumDecl); - } else if (name == tc.Context.Id_init) { - auto argumentNames = requirement->getFullName().getArgumentNames(); - if (argumentNames.size() == 1) { - if (argumentNames[0] == tc.Context.Id_stringValue) - return deriveCodingKey_enum_init_stringValue(tc, parentDecl, enumDecl); - else if (argumentNames[0] == tc.Context.Id_intValue) - return deriveCodingKey_enum_init_intValue(tc, parentDecl, enumDecl); - } - } - - tc.diagnose(requirement->getLoc(), - diag::broken_coding_key_requirement); + auto &C = tc.Context; + auto rawType = enumDecl->getRawType(); + auto name = requirement->getName(); + if (name == C.Id_stringValue) { + // Synthesize `var stringValue: String? { get }` + auto stringType = C.getStringDecl()->getDeclaredType(); + auto optionalStringType = OptionalType::get(OTK_Optional, stringType); + + auto synth = [rawType, stringType](AbstractFunctionDecl *getterDecl) { + if (rawType && rawType->isEqual(stringType)) { + // enum SomeStringEnum : String { + // case A, B, C + // @derived var stringValue: String? { + // return self.rawValue + // } + getterDecl->setBodySynthesizer(&deriveRawValueReturn); + } else { + // enum SomeEnum { + // case A, B, C + // @derived var stringValue: String? { + // switch self { + // case A: + // return "A" + // case B: + // return "B" + // case C: + // return "C" + // } + // } + // } + getterDecl->setBodySynthesizer(&deriveBodyCodingKey_enum_stringValue); + } + }; + + return deriveProperty(tc, parentDecl, enumDecl, optionalStringType, + C.Id_stringValue, synth); + + } else if (name == C.Id_intValue) { + // Synthesize `var intValue: Int? { get }` + auto intType = C.getIntDecl()->getDeclaredType(); + auto optionalIntType = OptionalType::get(OTK_Optional, intType); + + auto synth = [rawType, intType](AbstractFunctionDecl *getterDecl) { + if (rawType && rawType->isEqual(intType)) { + // enum SomeIntEnum : Int { + // case A = 1, B = 2, C = 3 + // @derived var intValue: Int? { + // return self.rawValue + // } + // } + getterDecl->setBodySynthesizer(&deriveRawValueReturn); + } else { + // enum SomeEnum { + // case A, B, C + // @derived var intValue: Int? { + // return nil + // } + // } + getterDecl->setBodySynthesizer(&deriveNilReturn); + } + }; + + return deriveProperty(tc, parentDecl, enumDecl, optionalIntType, + C.Id_intValue, synth); + } else if (name == C.Id_init) { + auto argumentNames = requirement->getFullName().getArgumentNames(); + if (argumentNames.size() == 1) { + if (argumentNames[0] == C.Id_stringValue) { + // Derive `init?(stringValue:)` + auto stringType = C.getStringDecl()->getDeclaredType(); + auto synth = [rawType, stringType](AbstractFunctionDecl *initDecl) { + if (rawType && rawType->isEqual(stringType)) { + // enum SomeStringEnum : String { + // case A = "a", B = "b", C = "c" + // @derived init?(stringValue: String) { + // self.init(rawValue: stringValue) + // } + // } + initDecl->setBodySynthesizer(&deriveRawValueInit); + } else { + // enum SomeEnum { + // case A, B, C + // @derived init?(stringValue: String) { + // switch stringValue { + // case "A": + // self = .A + // case "B": + // self = .B + // case "C": + // self = .C + // default: + // return nil + // } + // } + // } + initDecl->setBodySynthesizer(&deriveBodyCodingKey_init_stringValue); + } + }; + + return deriveInitDecl(tc, parentDecl, enumDecl, stringType, + C.Id_stringValue, synth); + } else if (argumentNames[0] == C.Id_intValue) { + // Synthesize `init?(intValue:)` + auto intType = C.getIntDecl()->getDeclaredType(); + auto synthesizer = [rawType, intType](AbstractFunctionDecl *initDecl) { + if (rawType && rawType->isEqual(intType)) { + // enum SomeIntEnum : Int { + // case A = 1, B = 2, C = 3 + // @derived init?(intValue: Int) { + // self.init(rawValue: intValue) + // } + // } + initDecl->setBodySynthesizer(&deriveRawValueInit); + } else { + // enum SomeEnum { + // case A, B, C + // @derived init?(intValue: Int) { + // return nil + // } + // } + initDecl->setBodySynthesizer(&deriveNilReturn); + } + }; + + return deriveInitDecl(tc, parentDecl, enumDecl, intType, C.Id_intValue, + synthesizer); + } + } + } + + tc.diagnose(requirement->getLoc(), diag::broken_coding_key_requirement); return nullptr; } From b98b68e62fd9f7b9ce566795def6a2ee94f140ad Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Tue, 21 Mar 2017 11:03:09 -0700 Subject: [PATCH 4/9] Don't decode a let var with a default value --- lib/Sema/DerivedConformanceCodable.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index b443bb6417892..019b20874286b 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -733,13 +733,16 @@ static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { // Now need to generate `x = try container.encode(Type.self, forKey: .x)` // for all existing properties. for (auto *elt : enumElements) { - // TODO: Don't decode a let var that has a default value. // Only ill-formed code would produce multiple results for this lookup. // This would get diagnosed later anyway, so we're free to only look at // the first result here. auto matchingVars = typeDecl->lookupDirect(DeclName(elt->getName())); auto *varDecl = cast(matchingVars[0]); + // Don't output a decode statement for a var let with a default value. + if (varDecl->isLet() && varDecl->getParentInitializer() != nullptr) + continue; + // Type.self (where Type === type(of: x) auto varType = varDecl->getType(); auto *metaTyRef = TypeExpr::createImplicit(varType, C); From 8ab7f877c72fb2cd6018757ff89feb8db2e83b17 Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Mon, 3 Apr 2017 09:39:55 -0700 Subject: [PATCH 5/9] Split derived conformance for Codable into Encodable and Decodable --- include/swift/AST/DiagnosticsSema.def | 6 +- include/swift/AST/KnownIdentifiers.def | 3 +- include/swift/AST/KnownProtocols.def | 3 +- lib/AST/ASTContext.cpp | 7 +- lib/AST/Decl.cpp | 7 +- lib/IRGen/GenMeta.cpp | 3 +- lib/Sema/DerivedConformanceCodable.cpp | 277 +++++++++++++++---------- lib/Sema/DerivedConformances.cpp | 12 +- lib/Sema/DerivedConformances.h | 18 +- lib/Sema/TypeCheckProtocol.cpp | 7 +- 10 files changed, 212 insertions(+), 131 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 2a8d767f677e0..00d390231d9ef 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1897,8 +1897,10 @@ ERROR(no_equal_overload_for_int,none, "no overload of '==' for Int", ()) ERROR(broken_coding_key_requirement,none, "CodingKey protocol is broken: unexpected requirement", ()) -ERROR(broken_codable_requirement,none, - "Codable protocol is broken: unexpected requirement", ()) +ERROR(broken_encodable_requirement,none, + "Encodable protocol is broken: unexpected requirement", ()) +ERROR(broken_decodable_requirement,none, + "Decodable protocol is broken: unexpected requirement", ()) // Dynamic Self ERROR(dynamic_self_non_method,none, diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 93e487c82458c..c5a8b9407863b 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -29,7 +29,6 @@ IDENTIFIER(Any) IDENTIFIER(atIndexedSubscript) IDENTIFIER_(bridgeToObjectiveC) IDENTIFIER_WITH_NAME(code_, "_code") -IDENTIFIER(Codable) IDENTIFIER(CodingKeys) IDENTIFIER(container) IDENTIFIER(CoreGraphics) @@ -38,11 +37,13 @@ IDENTIFIER(CGFloat) IDENTIFIER(CVarArg) IDENTIFIER(Darwin) IDENTIFIER(dealloc) +IDENTIFIER(Decodable) IDENTIFIER(decode) IDENTIFIER(Decoder) IDENTIFIER(decoder) IDENTIFIER(deinit) IDENTIFIER(Element) +IDENTIFIER(Encodable) IDENTIFIER(encode) IDENTIFIER(Encoder) IDENTIFIER(encoder) diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index ffd5a20c59cad..bf945dd167cb2 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -61,7 +61,8 @@ PROTOCOL(OptionSet) PROTOCOL_(BridgedNSError) PROTOCOL_(BridgedStoredNSError) PROTOCOL(CodingKey) -PROTOCOL(Codable) +PROTOCOL(Encodable) +PROTOCOL(Decodable) PROTOCOL_(ObjectiveCBridgeable) PROTOCOL_(DestructorSafeContainer) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 3243e798acc54..d4e3284edd353 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -739,13 +739,14 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { // Find all of the declarations with this name in the appropriate module. SmallVector results; - // _BridgedNSError, _BridgedStoredNSError, and _ErrorCodeProtocol - // are in the Foundation module. + // _BridgedNSError, _BridgedStoredNSError, _ErrorCodeProtocol, CodingKey, + // Encodable, and Decodable are all in the Foundation module. if (kind == KnownProtocolKind::BridgedNSError || kind == KnownProtocolKind::BridgedStoredNSError || kind == KnownProtocolKind::ErrorCodeProtocol || kind == KnownProtocolKind::CodingKey || - kind == KnownProtocolKind::Codable) { + kind == KnownProtocolKind::Encodable || + kind == KnownProtocolKind::Decodable) { ModuleDecl *foundation = const_cast(this)->getLoadedModule(Id_Foundation); if (!foundation) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index d12416b7dceb3..21bc66eb8ac2b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2113,8 +2113,11 @@ bool NominalTypeDecl::derivesProtocolConformance(ProtocolDecl *protocol) const { return false; } } else if (isa(this) || isa(this)) { - // Structs and classes can explicitly derive Codable conformance. - if (*knownProtocol == KnownProtocolKind::Codable) { + // Structs and classes can explicitly derive Encodable and Decodable + // conformance (explicitly meaning we can synthesize an implementation if + // a type conforms manually). + if (*knownProtocol == KnownProtocolKind::Encodable || + *knownProtocol == KnownProtocolKind::Decodable) { // FIXME: This is not actually correct. We cannot promise to always // provide a witness here for all structs and classes. Unfortunately, // figuring out whether this is actually possible requires much more diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 3593bdf9f4bd3..6f8b1a61d5e09 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5549,7 +5549,8 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::BridgedStoredNSError: case KnownProtocolKind::ErrorCodeProtocol: case KnownProtocolKind::CodingKey: - case KnownProtocolKind::Codable: + case KnownProtocolKind::Encodable: + case KnownProtocolKind::Decodable: return SpecialProtocol::None; } diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 019b20874286b..fc20e0b14b0e7 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -29,23 +29,41 @@ using namespace swift; using namespace DerivedConformance; /// Returns whether the type represented by the given ClassDecl inherits from a -/// type which is Codable. +/// type which conforms to the given protocol. /// -/// \param type The ClassDecl whose superclass to look up. -static bool superclassIsCodable(ClassDecl *type) { +/// \param type The \c ClassDecl whose superclass to look up. +/// +/// \param proto The protocol to check conformance for. +static bool inheritsConformanceTo(ClassDecl *type, ProtocolDecl *proto) { if (!type->hasSuperclass()) return false; auto &C = type->getASTContext(); - auto *codableProto = C.getProtocol(KnownProtocolKind::Codable); - auto *superclassDecl = type->getSuperclassDecl(); auto *superclassModule = superclassDecl->getModuleContext(); return (bool)superclassModule->lookupConformance(type->getSuperclass(), - codableProto, + proto, C.getLazyResolver()); } +/// Returns whether the superclass of the given class conforms to Encodable. +/// +/// \param type The \c ClassDecl whose superclass to check. +static bool superclassIsEncodable(ClassDecl *type) { + auto &C = type->getASTContext(); + return inheritsConformanceTo(type, + C.getProtocol(KnownProtocolKind::Encodable)); +} + +/// Returns whether the superclass of the given class conforms to Decodable. +/// +/// \param type The \c ClassDecl whose superclass to check. +static bool superclassIsDecodable(ClassDecl *type) { + auto &C = type->getASTContext(); + return inheritsConformanceTo(type, + C.getProtocol(KnownProtocolKind::Decodable)); +} + /// Looks up a type declaration with the given name in the Foundation module. /// /// Asserts that the Foundation module is loaded, that the given name does not @@ -83,24 +101,25 @@ static Type lookupFoundationType(ASTContext &C, Identifier name) { } /// Validates that all the variables declared in the given list of declarations -/// conform to the Codable protocol. +/// conform to the given protocol. /// /// Produces a diagnostic on the given typechecker for every var which does not /// conform. Calls a success callback for every var which does conform. /// -/// \param tc The typechecker to use in validating Codable conformance. +/// \param tc The typechecker to use in validating {En,Decodable} conformance. /// -/// \param context The DeclContext the var declarations belong to. +/// \param context The \c DeclContext the var declarations belong to. /// /// \param vars The var range to validate. /// +/// \param proto The protocol to check conformance to. +/// /// \param callback A callback to call on every valid var decl. template static bool -validateVarsConformToCodable(TypeChecker &tc, DeclContext *context, - NominalTypeDecl::StoredPropertyRange &vars, - ValidVarCallback &callback) { - auto codableProto = tc.Context.getProtocol(KnownProtocolKind::Codable); +validateVarsConformToProtocol(TypeChecker &tc, DeclContext *context, + NominalTypeDecl::StoredPropertyRange &vars, + ProtocolDecl *proto, ValidVarCallback &callback) { bool allConform = true; for (auto varDecl : vars) { // If the decl doesn't yet have a type, we may be seeing it before the type @@ -123,7 +142,7 @@ validateVarsConformToCodable(TypeChecker &tc, DeclContext *context, // If the var decl didn't validate, it may still not have a type; confirm it // has a type before ensuring the type conforms to Codable. if (!varDecl->hasType() || - !tc.conformsToProtocol(varDecl->getType(), codableProto, context, + !tc.conformsToProtocol(varDecl->getType(), proto, context, ConformanceCheckFlags::Used)) { // TODO: We should produce a diagnostic note here explaining that we found // a var not conforming to Codable. @@ -140,13 +159,16 @@ validateVarsConformToCodable(TypeChecker &tc, DeclContext *context, /// Validates the given CodingKeys enum decl by ensuring its cases are a 1-to-1 /// match with the stored vars of the given type. /// -/// \param tc The typechecker to use in validating Codable conformance. +/// \param tc The typechecker to use in validating {En,Decodable} conformance. +/// +/// \param codingKeysDecl The \c CodingKeys enum decl to validate. /// -/// \param codingKeysDecl The CodingKeys enum decl to validate. +/// \param type The nominal type decl to validate the \c CodingKeys against. /// -/// \param type The nominal type decl to validate the CodingKeys against. +/// \param proto The {En,De}codable protocol to validate all the keys conform +/// to. static bool validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, - NominalTypeDecl *type) { + NominalTypeDecl *type, ProtocolDecl *proto) { // Look through all var decls in the given type. // * Filter out lazy/computed vars (currently already done by // getStoredProperties). @@ -166,8 +188,8 @@ static bool validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, names.insert(varDecl->getName()); }; - if (!validateVarsConformToCodable(tc, type->getDeclContext(), - storedProperties, validVarCallback)) + if (!validateVarsConformToProtocol(tc, type->getDeclContext(), + storedProperties, proto, validVarCallback)) return false; for (auto elt : codingKeysDecl->getAllElements()) { @@ -188,19 +210,23 @@ static bool validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl, return names.empty(); } -/// Returns whether the given type has a valid nested CodingKeys enum. +/// Returns whether the given type has a valid nested \c CodingKeys enum. /// -/// If the type has an invalid CodingKeys entity, produces diagnostics to +/// If the type has an invalid \c CodingKeys entity, produces diagnostics to /// complain about the error. In this case, the error result will be true -- in /// the case where we don't have a valid CodingKeys enum and have produced /// diagnostics here, we don't want to then attempt to synthesize a CodingKeys /// enum. /// -/// \param tc The typechecker to use in validating Codable conformance. +/// \param tc The typechecker to use in validating {En,Decodable} conformance. /// -/// \param type The type decl whose nested CodingKeys type to validate. +/// \param type The type decl whose nested \c CodingKeys type to validate. +/// +/// \param proto The {En,De}codable protocol to ensure the properties matching +/// the keys conform to. static std::pair -hasValidCodingKeysEnum(TypeChecker &tc, NominalTypeDecl *type) { +hasValidCodingKeysEnum(TypeChecker &tc, NominalTypeDecl *type, + ProtocolDecl *proto) { auto &C = tc.Context; auto codingKeysDecls = type->lookupDirect(DeclName(C.Id_CodingKeys)); if (codingKeysDecls.empty()) @@ -247,28 +273,33 @@ hasValidCodingKeysEnum(TypeChecker &tc, NominalTypeDecl *type) { return {/* has type? */ true, /* error? */ true}; } - bool valid = validateCodingKeysEnum(tc, codingKeysEnum, type); + bool valid = validateCodingKeysEnum(tc, codingKeysEnum, type, proto); return {/* has type? */ true, /* error? */ !valid}; } -/// Synthesizes a new CodingKeys enum based on the Codable members of the given -/// type (nullptr if unable to synthesize). +/// Synthesizes a new \c CodingKeys enum based on the {En,De}codable members of +/// the given type (\c nullptr if unable to synthesize). /// /// If able to synthesize the enum, adds it directly to \c type. /// -/// \param tc The typechecker to use in validating Codable conformance. +/// \param tc The typechecker to use in validating {En,De}codable conformance. /// -/// \param type The nominal type decl whose nested CodingKeys type to +/// \param type The nominal type decl whose nested \c CodingKeys type to /// synthesize. +/// +/// \param proto The {En,De}codable protocol to validate all the keys conform +/// to. static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, - NominalTypeDecl *type) { + NominalTypeDecl *type, + ProtocolDecl *proto) { auto &C = tc.Context; auto *typeDC = cast(type); // We want to look through all the var declarations of this type to create // enum cases based on those var names. - auto *proto = C.getProtocol(KnownProtocolKind::CodingKey); - TypeLoc protoTypeLoc[1] = {TypeLoc::withoutLoc(proto->getDeclaredType())}; + auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); + auto *codingKeyType = codingKeyProto->getDeclaredType(); + TypeLoc protoTypeLoc[1] = {TypeLoc::withoutLoc(codingKeyType)}; MutableArrayRef inherited = C.AllocateCopy(protoTypeLoc); auto *enumDecl = new (C) EnumDecl(SourceLoc(), C.Id_CodingKeys, SourceLoc(), @@ -279,10 +310,11 @@ static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, auto *enumDC = cast(enumDecl); auto *mutableEnumDC = cast(enumDecl); - // For classes which inherit from something Codable, we provide case `super` - // as the first key (to be used in encoding super). + // For classes which inherit from something Encodable or Decodable, we + // provide case `super` as the first key (to be used in encoding super). auto *classDecl = dyn_cast(type); - if (classDecl && superclassIsCodable(classDecl)) { + if (classDecl && + (superclassIsEncodable(classDecl) || superclassIsDecodable(classDecl))) { // TODO: Ensure the class doesn't already have or inherit a variable named // "`super`"; otherwise we will generate an invalid enum. In that case, // diagnose and bail. @@ -294,7 +326,7 @@ static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, } // Each of these vars needs a case in the enum. For each var decl, if the type - // conforms to Codable, add it to the enum. + // conforms to {En,De}codable, add it to the enum. auto storedProperties = type->getStoredProperties(/*skipInaccessible=*/true); auto validVarCallback = [&C, &enumDC, &mutableEnumDC](VarDecl *varDecl) { auto *elt = new (C) EnumElementDecl(SourceLoc(), varDecl->getName(), @@ -304,8 +336,8 @@ static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, mutableEnumDC->addMember(elt); }; - if (!validateVarsConformToCodable(tc, type->getDeclContext(), - storedProperties, validVarCallback)) + if (!validateVarsConformToProtocol(tc, type->getDeclContext(), + storedProperties, proto, validVarCallback)) return nullptr; // Forcibly derive conformance to CodingKey. @@ -318,20 +350,23 @@ static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, /// Creates a new var decl representing /// -/// let container : containerBase +/// var/let container : containerBase /// -/// containerBase is the name of the type to use as the base (either -/// KeyedEncodingContainer or KeyedDecodingContainer). +/// \c containerBase is the name of the type to use as the base (either +/// \c KeyedEncodingContainer or \c KeyedDecodingContainer). /// /// \param C The AST context to create the decl in. /// -/// \param DC The DeclContext to create the decl in. +/// \param DC The \c DeclContext to create the decl in. /// /// \param containerBase The name of the generic type to bind the key type in. /// /// \param keyType The key type to bind to the container type. +/// +/// \param isLet Whether to declare the variable as immutable. static VarDecl *createKeyedContainer(ASTContext &C, DeclContext *DC, - Identifier containerBase, Type keyType) { + Identifier containerBase, Type keyType, + bool isLet) { // Look up Keyed*Container auto *keyedContainerDecl = cast(lookupFoundationTypeDecl(C, containerBase)); @@ -342,7 +377,7 @@ static VarDecl *createKeyedContainer(ASTContext &C, DeclContext *DC, C.AllocateCopy(boundType)); // let container : Keyed*Container - auto *containerDecl = new (C) VarDecl(/*IsStatic=*/false, /*IsLet=*/true, + auto *containerDecl = new (C) VarDecl(/*IsStatic=*/false, /*IsLet=*/isLet, /*IsCaptureList=*/false, SourceLoc(), C.Id_container, containerType, DC); containerDecl->setImplicit(); @@ -350,13 +385,13 @@ static VarDecl *createKeyedContainer(ASTContext &C, DeclContext *DC, return containerDecl; } -/// Creates a new CallExpr representing +/// Creates a new \c CallExpr representing /// /// base.container(keyedBy: CodingKeys.self) /// /// \param C The AST context to create the expression in. /// -/// \param DC The DeclContext to create any decls in. +/// \param DC The \c DeclContext to create any decls in. /// /// \param base The base expression to make the call on. /// @@ -398,7 +433,7 @@ static CallExpr *createContainerKeyedByCall(ASTContext &C, DeclContext *DC, /// Synthesizes the body for `func encode(to encoder: Encoder) throws`. /// /// \param encodeDecl The function decl whose body to synthesize. -static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { +static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl) { // struct Foo : Codable { // var x: Int // var y: String @@ -410,7 +445,7 @@ static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { // } // // @derived func encode(to encoder: Encoder) throws { - // let container = encoder.container(keyedBy: CodingKeys.self) + // var container = encoder.container(keyedBy: CodingKeys.self) // try container.encode(x, forKey: .x) // try container.encode(y, forKey: .y) // } @@ -440,7 +475,7 @@ static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { auto codingKeysType = codingKeysEnum->getDeclaredType(); auto *containerDecl = createKeyedContainer(C, funcDC, C.Id_KeyedEncodingContainer, - codingKeysType); + codingKeysType, /*isLet=*/false); auto *containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), DeclNameLoc(), /*Implicit=*/true, @@ -515,7 +550,7 @@ static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { // Classes which inherit from something Codable should encode super as well. auto *classDecl = dyn_cast(typeDecl); - if (classDecl && superclassIsCodable(classDecl)) { + if (classDecl && superclassIsEncodable(classDecl)) { // Need to generate `try super.encode(to: container.superEncoder())` // superEncoder() @@ -566,8 +601,8 @@ static void deriveBodyCodable_encode(AbstractFunctionDecl *encodeDecl) { /// \param parentDecl The parent declaration of the type. /// /// \param type The nominal type to synthesize the function for. -static FuncDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *type) { +static FuncDecl *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl, + NominalTypeDecl *type) { auto &C = tc.Context; auto *typeDC = cast(type); @@ -612,12 +647,12 @@ static FuncDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, nullptr, params, TypeLoc::withoutLoc(returnType), typeDC); encodeDecl->setImplicit(); - encodeDecl->setBodySynthesizer(deriveBodyCodable_encode); + encodeDecl->setBodySynthesizer(deriveBodyEncodable_encode); - // This method should be marked as 'override' for classes inheriting Codable + // This method should be marked as 'override' for classes inheriting Encodable // conformance from a parent class. auto *classDecl = dyn_cast(type); - if (classDecl && superclassIsCodable(classDecl)) { + if (classDecl && superclassIsEncodable(classDecl)) { auto *attr = new (C) SimpleDeclAttr(/*IsImplicit=*/true); encodeDecl->getAttrs().add(attr); } @@ -652,7 +687,7 @@ static FuncDecl *deriveCodable_encode(TypeChecker &tc, Decl *parentDecl, /// Synthesizes the body for `init(from decoder: Decoder) throws`. /// /// \param initDecl The function decl whose body to synthesize. -static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { +static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl) { // struct Foo : Codable { // var x: Int // var y: String @@ -690,8 +725,9 @@ static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { // let container : KeyedDecodingContainer auto codingKeysType = codingKeysEnum->getDeclaredType(); - auto *containerDecl = createKeyedContainer( - C, funcDC, C.Id_KeyedEncodingContainer, codingKeysType); + auto *containerDecl = createKeyedContainer(C, funcDC, + C.Id_KeyedEncodingContainer, + codingKeysType, /*isLet=*/true); auto *containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), DeclNameLoc(), /*Implicit=*/true, @@ -781,9 +817,9 @@ static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { } } - // Classes which inherit from something Codable should decode super as well. + // Classes which inherit from something Decodable should decode super as well. auto *classDecl = dyn_cast(typeDecl); - if (classDecl && superclassIsCodable(classDecl)) { + if (classDecl && superclassIsDecodable(classDecl)) { // Need to generate `try super.init(from: container.superDecoder())` // superDecoder() @@ -834,8 +870,8 @@ static void deriveBodyCodable_init(AbstractFunctionDecl *initDecl) { /// \param parentDecl The parent declaration of the type. /// /// \param type The nominal type to synthesize the function for. -static ValueDecl *deriveCodable_init(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *type) { +static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl, + NominalTypeDecl *type) { auto &C = tc.Context; auto *typeDC = cast(type); @@ -888,7 +924,7 @@ static ValueDecl *deriveCodable_init(TypeChecker &tc, Decl *parentDecl, /*Throws=*/true, /*ThrowsLoc=*/SourceLoc(), selfDecl, paramList, /*GenericParams=*/nullptr, typeDC); initDecl->setImplicit(); - initDecl->setBodySynthesizer(deriveBodyCodable_init); + initDecl->setBodySynthesizer(deriveBodyDecodable_init); // This constructor should be marked as `required` for non-final classes. if (isa(type) && type->getAttrs().hasAttribute()) { @@ -928,18 +964,21 @@ static ValueDecl *deriveCodable_init(TypeChecker &tc, Decl *parentDecl, return initDecl; } -/// Returns whether the given type is valid for synthesizing Codable. +/// Returns whether the given type is valid for synthesizing {En,De}codable. /// -/// Checks to see whether the given type has a valid CodingKeys enum, and if +/// Checks to see whether the given type has a valid \c CodingKeys enum, and if /// not, attempts to synthesize one for it. /// -/// \param tc The type checker to use in checking for Codable conformance. +/// \param tc The typechecker to use in validating {En,Decodable} conformance. /// /// \param type The type to validate. -static bool canSynthesizeCodable(TypeChecker &tc, NominalTypeDecl *type) { +/// +/// \param proto The *codable protocol to check for validity. +static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *type, + ProtocolDecl *proto) { // First, look up if the type has a valid CodingKeys enum we can use. bool hasType, error; - std::tie(hasType, error) = hasValidCodingKeysEnum(tc, type); + std::tie(hasType, error) = hasValidCodingKeysEnum(tc, type, proto); // We found a type, but it wasn't valid. if (error) @@ -947,7 +986,7 @@ static bool canSynthesizeCodable(TypeChecker &tc, NominalTypeDecl *type) { // We can try to synthesize a type here. if (!hasType) { - auto *synthesizedEnum = synthesizeCodingKeysEnum(tc, type); + auto *synthesizedEnum = synthesizeCodingKeysEnum(tc, type, proto); if (!synthesizedEnum) return false; } @@ -955,43 +994,69 @@ static bool canSynthesizeCodable(TypeChecker &tc, NominalTypeDecl *type) { return true; } -ValueDecl *DerivedConformance::deriveCodable(TypeChecker &tc, Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement) { - // We can only synthesize Codable for structs and classes. - if (!isa(type) && !isa(type)) - return nullptr; +ValueDecl *DerivedConformance::deriveEncodable(TypeChecker &tc, + Decl *parentDecl, + NominalTypeDecl *type, + ValueDecl *requirement) { + // We can only synthesize Encodable for structs and classes. + if (!isa(type) && !isa(type)) + return nullptr; + + if (requirement->getName() != tc.Context.Id_encode) { + // Unknown requirement. + tc.diagnose(requirement->getLoc(), diag::broken_encodable_requirement); + return nullptr; + } - // Check other preconditions for synthesized conformance. - // This synthesizes a CodingKeys enum if possible. - bool canSynthesize = canSynthesizeCodable(tc, type); - - diag::RequirementKind reqKind; - auto name = requirement->getName(); - if (name == tc.Context.Id_init) { - reqKind = diag::RequirementKind::Constructor; - if (canSynthesize) - return deriveCodable_init(tc, parentDecl, type); - } else if (name == tc.Context.Id_encode) { - reqKind = diag::RequirementKind::Func; - if (canSynthesize) - return deriveCodable_encode(tc, parentDecl, type); - } else { - // Unknown requirement. - tc.diagnose(requirement->getLoc(), diag::broken_codable_requirement); + // Check other preconditions for synthesized conformance. + // This synthesizes a CodingKeys enum if possible. + auto encodableProto = tc.Context.getProtocol(KnownProtocolKind::Encodable); + if (canSynthesize(tc, type, encodableProto)) + return deriveEncodable_encode(tc, parentDecl, type); + + // Known protocol requirement but could not synthesize. + // FIXME: We have to output at least one error diagnostic here because we + // returned true from NominalTypeDecl::derivesProtocolConformance; if we + // don't, we expect to return a witness here later and crash on an + // assertion. Producing an error stops compilation before then. + auto encodableType = encodableProto->getDeclaredType(); + tc.diagnose(type, diag::type_does_not_conform, type->getDeclaredType(), + encodableType); + tc.diagnose(requirement, diag::no_witnesses, diag::RequirementKind::Func, + requirement->getFullName(), encodableType, /*AddFixIt=*/false); return nullptr; - } +} - // Known protocol requirement but could not synthesize. - // FIXME: We have to output at least one error diagnostic here because we - // returned true from NominalTypeDecl::derivesProtocolConformance; if we - // don't, we expect to return a witness here later and crash on an assertion. - // Producing an error stops compilation before then. - auto codableProto = tc.Context.getProtocol(KnownProtocolKind::Codable); - auto codableType = codableProto->getDeclaredType(); - tc.diagnose(type, diag::type_does_not_conform, type->getDeclaredType(), - codableType); - tc.diagnose(requirement, diag::no_witnesses, reqKind, - requirement->getFullName(), codableType, /*AddFixIt=*/false); - return nullptr; +ValueDecl *DerivedConformance::deriveDecodable(TypeChecker &tc, + Decl *parentDecl, + NominalTypeDecl *type, + ValueDecl *requirement) { + // We can only synthesize Encodable for structs and classes. + if (!isa(type) && !isa(type)) + return nullptr; + + if (requirement->getName() != tc.Context.Id_init) { + // Unknown requirement. + tc.diagnose(requirement->getLoc(), diag::broken_decodable_requirement); + return nullptr; + } + + // Check other preconditions for synthesized conformance. + // This synthesizes a CodingKeys enum if possible. + auto decodableProto = tc.Context.getProtocol(KnownProtocolKind::Decodable); + if (canSynthesize(tc, type, decodableProto)) + return deriveDecodable_init(tc, parentDecl, type); + + // Known protocol requirement but could not synthesize. + // FIXME: We have to output at least one error diagnostic here because we + // returned true from NominalTypeDecl::derivesProtocolConformance; if we + // don't, we expect to return a witness here later and crash on an + // assertion. Producing an error stops compilation before then. + auto decodableType = decodableProto->getDeclaredType(); + tc.diagnose(type, diag::type_does_not_conform, type->getDeclaredType(), + decodableType); + tc.diagnose(requirement, diag::no_witnesses, + diag::RequirementKind::Constructor, requirement->getFullName(), + decodableType, /*AddFixIt=*/false); + return nullptr; } diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 5ab3a6084f371..02b1563bfc299 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -74,11 +74,11 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, if (func->isOperator() && name.getBaseName().str() == "==") return getRequirement(KnownProtocolKind::Equatable); - // Codable.encode(to: Encoder) + // Encodable.encode(to: Encoder) if (name.isCompoundName() && name.getBaseName() == ctx.Id_encode) { auto argumentNames = name.getArgumentNames(); if (argumentNames.size() == 1 && argumentNames[0] == ctx.Id_to) - return getRequirement(KnownProtocolKind::Codable); + return getRequirement(KnownProtocolKind::Encodable); } return nullptr; @@ -97,9 +97,9 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, argumentNames[0] == ctx.Id_intValue)) return getRequirement(KnownProtocolKind::CodingKey); - // Codable.init(from: Decoder) + // Decodable.init(from: Decoder) if (argumentNames[0] == ctx.Id_from) - return getRequirement(KnownProtocolKind::Codable); + return getRequirement(KnownProtocolKind::Decodable); } return nullptr; @@ -111,10 +111,6 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, if (name.isSimpleName(ctx.Id_RawValue)) return getRequirement(KnownProtocolKind::RawRepresentable); - // Codable.CodingKeys - if (name.isSimpleName(ctx.Id_CodingKeys)) - return getRequirement(KnownProtocolKind::Codable); - return nullptr; } diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 79f3c97cb33c6..9b56a26890bfe 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -104,13 +104,21 @@ ValueDecl *deriveCodingKey(TypeChecker &tc, NominalTypeDecl *type, ValueDecl *requirement); -/// Derive a Codable requirement for a struct type. +/// Derive an Encodable requirement for a struct type. /// /// \returns the derived member, which will also be added to the type. -ValueDecl *deriveCodable(TypeChecker &tc, - Decl *parentDecl, - NominalTypeDecl *type, - ValueDecl *requirement); +ValueDecl *deriveEncodable(TypeChecker &tc, + Decl *parentDecl, + NominalTypeDecl *type, + ValueDecl *requirement); + +/// Derive a Decodable requirement for a struct type. +/// +/// \returns the derived member, which will also be added to the type. +ValueDecl *deriveDecodable(TypeChecker &tc, + Decl *parentDecl, + NominalTypeDecl *type, + ValueDecl *requirement); /// Declare a getter for a derived property. FuncDecl *declareDerivedPropertyGetter(TypeChecker &tc, diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 5a9bc24d78758..cc4bcf1470527 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -6237,8 +6237,11 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, case KnownProtocolKind::CodingKey: return DerivedConformance::deriveCodingKey(*this, Decl, TypeDecl, Requirement); - case KnownProtocolKind::Codable: - return DerivedConformance::deriveCodable(*this, Decl, TypeDecl, Requirement); + case KnownProtocolKind::Encodable: + return DerivedConformance::deriveEncodable(*this, Decl, TypeDecl, Requirement); + + case KnownProtocolKind::Decodable: + return DerivedConformance::deriveDecodable(*this, Decl, TypeDecl, Requirement); default: return nullptr; From f4d7d43ca696bbd265fae528e2d70743bb5d6fd7 Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Mon, 3 Apr 2017 09:40:10 -0700 Subject: [PATCH 6/9] Make CodingKey.stringValue non-optional --- lib/Sema/DerivedConformanceCodingKey.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp index 803a70561b841..a519e7eefa7f7 100644 --- a/lib/Sema/DerivedConformanceCodingKey.cpp +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -426,10 +426,8 @@ ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, auto rawType = enumDecl->getRawType(); auto name = requirement->getName(); if (name == C.Id_stringValue) { - // Synthesize `var stringValue: String? { get }` + // Synthesize `var stringValue: String { get }` auto stringType = C.getStringDecl()->getDeclaredType(); - auto optionalStringType = OptionalType::get(OTK_Optional, stringType); - auto synth = [rawType, stringType](AbstractFunctionDecl *getterDecl) { if (rawType && rawType->isEqual(stringType)) { // enum SomeStringEnum : String { @@ -456,7 +454,7 @@ ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, } }; - return deriveProperty(tc, parentDecl, enumDecl, optionalStringType, + return deriveProperty(tc, parentDecl, enumDecl, stringType, C.Id_stringValue, synth); } else if (name == C.Id_intValue) { From 369a89b08e239b81e12aeb66334b80f295737081 Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Mon, 3 Apr 2017 14:51:55 -0700 Subject: [PATCH 7/9] Fix stringValue nil return for empty CodingKey enums --- lib/Sema/DerivedConformanceCodingKey.cpp | 64 +++++++++++++----------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp index a519e7eefa7f7..dbda2193c0601 100644 --- a/lib/Sema/DerivedConformanceCodingKey.cpp +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -244,7 +244,7 @@ static void deriveBodyCodingKey_enum_stringValue(AbstractFunctionDecl *strValDecl) { // enum SomeEnum { // case A, B, C - // @derived var stringValue: String? { + // @derived var stringValue: String { // switch self { // case A: // return "A" @@ -261,38 +261,44 @@ deriveBodyCodingKey_enum_stringValue(AbstractFunctionDecl *strValDecl) { auto *enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); Type enumType = parentDC->getDeclaredTypeInContext(); + BraceStmt *body = nullptr; auto elements = enumDecl->getAllElements(); if (elements.empty() /* empty enum */) { - deriveNilReturn(strValDecl); - return; - } - - SmallVector cases; - for (auto *elt : elements) { - auto *pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, nullptr); - pat->setImplicit(); + // return "" + auto *emptyStringExpr = new (C) StringLiteralExpr("", SourceRange(), + /*Implicit=*/true); + auto *returnStmt = new (C) ReturnStmt(SourceLoc(), emptyStringExpr); + body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), + SourceLoc()); + } else { + SmallVector cases; + for (auto *elt : elements) { + auto *pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), + SourceLoc(), SourceLoc(), + Identifier(), elt, nullptr); + pat->setImplicit(); + + auto labelItem = CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), + nullptr); - auto labelItem = CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), - nullptr); + auto *caseValue = new (C) StringLiteralExpr(elt->getNameStr(), + SourceRange(), + /*Implicit=*/true); + auto *returnStmt = new (C) ReturnStmt(SourceLoc(), caseValue); + auto *caseBody = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), + SourceLoc()); + cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, + /*HasBoundDecls=*/false, SourceLoc(), + caseBody)); + } - auto *returnExpr = new (C) StringLiteralExpr(elt->getNameStr(), - SourceRange(), - /*Implicit=*/true); - auto *returnStmt = new (C) ReturnStmt(SourceLoc(), returnExpr); - auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), - SourceLoc()); - cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, - /*HasBoundDecls=*/false, SourceLoc(), - body)); + auto *selfRef = createSelfDeclRef(strValDecl); + auto *switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), + selfRef, SourceLoc(), cases, + SourceLoc(), C); + body = BraceStmt::create(C, SourceLoc(), ASTNode(switchStmt), SourceLoc()); } - auto *selfRef = createSelfDeclRef(strValDecl); - auto *switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), selfRef, - SourceLoc(), cases, SourceLoc(), C); - auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(switchStmt), - SourceLoc()); strValDecl->setBody(body); } @@ -432,14 +438,14 @@ ValueDecl *DerivedConformance::deriveCodingKey(TypeChecker &tc, if (rawType && rawType->isEqual(stringType)) { // enum SomeStringEnum : String { // case A, B, C - // @derived var stringValue: String? { + // @derived var stringValue: String { // return self.rawValue // } getterDecl->setBodySynthesizer(&deriveRawValueReturn); } else { // enum SomeEnum { // case A, B, C - // @derived var stringValue: String? { + // @derived var stringValue: String { // switch self { // case A: // return "A" From a411077d4815287e51b64e3565006446c96cc3fb Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Mon, 3 Apr 2017 14:52:59 -0700 Subject: [PATCH 8/9] Update CodingKey unit tests with non-optional stringValue --- .../coding/enum_coding_key_int_raw_type.swift | 2 +- .../coding/enum_coding_key_no_raw_type.swift | 2 +- .../enum_coding_key_partial_implementation.swift | 14 +++++++------- .../coding/enum_coding_key_string_raw_type.swift | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/decl/protocol/special/coding/enum_coding_key_int_raw_type.swift b/test/decl/protocol/special/coding/enum_coding_key_int_raw_type.swift index 91c3a334dc7c4..a49c15f21b1bf 100644 --- a/test/decl/protocol/special/coding/enum_coding_key_int_raw_type.swift +++ b/test/decl/protocol/special/coding/enum_coding_key_int_raw_type.swift @@ -32,7 +32,7 @@ enum PartialIntKey : Int, CodingKey { return self.rawValue + 1 } - var stringValue: String? { + var stringValue: String { switch self { case .a: return "A" case .b: return "B" diff --git a/test/decl/protocol/special/coding/enum_coding_key_no_raw_type.swift b/test/decl/protocol/special/coding/enum_coding_key_no_raw_type.swift index d0411b097094f..f43724e2f776c 100644 --- a/test/decl/protocol/special/coding/enum_coding_key_no_raw_type.swift +++ b/test/decl/protocol/special/coding/enum_coding_key_no_raw_type.swift @@ -31,7 +31,7 @@ enum PartialImplicitKey : CodingKey { } } - var stringValue: String? { + var stringValue: String { switch self { case .a: return "A" case .b: return "B" diff --git a/test/decl/protocol/special/coding/enum_coding_key_partial_implementation.swift b/test/decl/protocol/special/coding/enum_coding_key_partial_implementation.swift index 7c1ed7277cca7..204b23ae5b534 100644 --- a/test/decl/protocol/special/coding/enum_coding_key_partial_implementation.swift +++ b/test/decl/protocol/special/coding/enum_coding_key_partial_implementation.swift @@ -17,7 +17,7 @@ let _ = NoRawTypeKey1(intValue: 0) enum NoRawTypeKey2 : CodingKey { case a, b, c - var stringValue: String? { return nil } + var stringValue: String { return "" } } let _ = NoRawTypeKey2.a.stringValue @@ -57,7 +57,7 @@ let _ = NoRawTypeKey5(intValue: 0) enum NoRawTypeKey6 : CodingKey { case a, b, c - var stringValue: String? { return nil } + var stringValue: String { return "" } var intValue: Int? { return nil } } @@ -68,7 +68,7 @@ let _ = NoRawTypeKey6(intValue: 0) enum NoRawTypeKey7 : CodingKey { case a, b, c - var stringValue: String? { return nil } + var stringValue: String { return "" } init?(stringValue: String) { return nil } } @@ -79,7 +79,7 @@ let _ = NoRawTypeKey7(intValue: 0) enum NoRawTypeKey8 : CodingKey { case a, b, c - var stringValue: String? { return nil } + var stringValue: String { return "" } init?(intValue: Int) { return nil } } @@ -112,7 +112,7 @@ let _ = NoRawTypeKey10(intValue: 0) enum NoRawTypeKey11 : CodingKey { case a, b, c - var stringValue: String? { return nil } + var stringValue: String { return "" } var intValue: Int? { return nil } init?(stringValue: String) { return nil } } @@ -124,7 +124,7 @@ let _ = NoRawTypeKey11(intValue: 0) enum NoRawTypeKey12 : CodingKey { case a, b, c - var stringValue: String? { return nil } + var stringValue: String { return "" } var intValue: Int? { return nil } init?(intValue: Int) { return nil } } @@ -139,7 +139,7 @@ let _ = NoRawTypeKey12(intValue: 0) // existing methods). enum NoRawTypeKey13 : CodingKey { case a, b, c - var stringValue: String? { return nil } + var stringValue: String { return "" } var intValue: Int? { return nil } init?(stringValue: String) { return nil } init?(intValue: Int) { return nil } diff --git a/test/decl/protocol/special/coding/enum_coding_key_string_raw_type.swift b/test/decl/protocol/special/coding/enum_coding_key_string_raw_type.swift index 4f156d1f3fce8..bb5c86c89ce5f 100644 --- a/test/decl/protocol/special/coding/enum_coding_key_string_raw_type.swift +++ b/test/decl/protocol/special/coding/enum_coding_key_string_raw_type.swift @@ -35,7 +35,7 @@ enum PartialStringKey : String, CodingKey { } } - var stringValue: String? { + var stringValue: String { switch self { case .a: return "x" case .b: return "y" From a1902cbf40be7448c1a944072bbaf5cee8a1a352 Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Mon, 24 Apr 2017 16:49:26 -0700 Subject: [PATCH 9/9] Sink core protocols and types into the Swift stdlib All referenced known types are now part of the Swift stdlib, rather than Foundation --- include/swift/AST/KnownStdlibTypes.def | 5 ++ lib/AST/ASTContext.cpp | 9 ++-- lib/Sema/DerivedConformanceCodable.cpp | 58 ++++-------------------- lib/Sema/DerivedConformanceCodingKey.cpp | 4 +- 4 files changed, 19 insertions(+), 57 deletions(-) diff --git a/include/swift/AST/KnownStdlibTypes.def b/include/swift/AST/KnownStdlibTypes.def index 1fe89d46c546f..1d4ad85d35aab 100644 --- a/include/swift/AST/KnownStdlibTypes.def +++ b/include/swift/AST/KnownStdlibTypes.def @@ -65,4 +65,9 @@ KNOWN_STDLIB_TYPE_DECL(Unmanaged, NominalTypeDecl, 1) KNOWN_STDLIB_TYPE_DECL(Never, NominalTypeDecl, 0) +KNOWN_STDLIB_TYPE_DECL(Encoder, ProtocolDecl, 1) +KNOWN_STDLIB_TYPE_DECL(Decoder, ProtocolDecl, 1) +KNOWN_STDLIB_TYPE_DECL(KeyedEncodingContainer, NominalTypeDecl, 1) +KNOWN_STDLIB_TYPE_DECL(KeyedDecodingContainer, NominalTypeDecl, 1) + #undef KNOWN_STDLIB_TYPE_DECL diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index d4e3284edd353..86ea43d153475 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -739,14 +739,11 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { // Find all of the declarations with this name in the appropriate module. SmallVector results; - // _BridgedNSError, _BridgedStoredNSError, _ErrorCodeProtocol, CodingKey, - // Encodable, and Decodable are all in the Foundation module. + // _BridgedNSError, _BridgedStoredNSError, and _ErrorCodeProtocol are all in + // the Foundation module. if (kind == KnownProtocolKind::BridgedNSError || kind == KnownProtocolKind::BridgedStoredNSError || - kind == KnownProtocolKind::ErrorCodeProtocol || - kind == KnownProtocolKind::CodingKey || - kind == KnownProtocolKind::Encodable || - kind == KnownProtocolKind::Decodable) { + kind == KnownProtocolKind::ErrorCodeProtocol) { ModuleDecl *foundation = const_cast(this)->getLoadedModule(Id_Foundation); if (!foundation) diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index fc20e0b14b0e7..483bf935176ff 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// // -// This file implements explicit derivation of the Foundation Codable protocol -// for a struct or class. +// This file implements explicit derivation of the Encodable and Decodable +// protocols for a struct or class. // //===----------------------------------------------------------------------===// // @@ -64,42 +64,6 @@ static bool superclassIsDecodable(ClassDecl *type) { C.getProtocol(KnownProtocolKind::Decodable)); } -/// Looks up a type declaration with the given name in the Foundation module. -/// -/// Asserts that the Foundation module is loaded, that the given name does not -/// refer to more than one type, and that the entity with the given name refers -/// to a type instead of a different type of declaration. -/// -/// \param C The AST context to perform the lookup in. -/// -/// \param name The name of the type declaration to look up. -static TypeDecl *lookupFoundationTypeDecl(ASTContext &C, Identifier name) { - auto *foundationModule = C.getLoadedModule(C.Id_Foundation); - assert(foundationModule && "Foundation module must be loaded."); - - SmallVector results; - foundationModule->lookupMember(results, cast(foundationModule), - DeclName(name), Identifier()); - assert(results.size() == 1 && "Ambiguous/missing type."); - - auto *typeDecl = dyn_cast(results[0]); - assert(typeDecl && "Found non-type decl."); - return typeDecl; -} - -/// Looks up a type with the given name in the Foundation module. -/// -/// Asserts that the Foundation module is loaded, that the given name does not -/// refer to more than one type, and that the entity with the given name refers -/// to a type instead of a different type of declaration. -/// -/// \param C The AST context to perform the lookup in. -/// -/// \param name The name of the type to look up. -static Type lookupFoundationType(ASTContext &C, Identifier name) { - return lookupFoundationTypeDecl(C, name)->getDeclaredInterfaceType(); -} - /// Validates that all the variables declared in the given list of declarations /// conform to the given protocol. /// @@ -359,18 +323,14 @@ static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc, /// /// \param DC The \c DeclContext to create the decl in. /// -/// \param containerBase The name of the generic type to bind the key type in. +/// \param keyedContainerDecl The generic type to bind the key type in. /// /// \param keyType The key type to bind to the container type. /// /// \param isLet Whether to declare the variable as immutable. static VarDecl *createKeyedContainer(ASTContext &C, DeclContext *DC, - Identifier containerBase, Type keyType, - bool isLet) { - // Look up Keyed*Container - auto *keyedContainerDecl = - cast(lookupFoundationTypeDecl(C, containerBase)); - + NominalTypeDecl *keyedContainerDecl, + Type keyType, bool isLet) { // Bind Keyed*Container to Keyed*Container Type boundType[1] = {keyType}; auto containerType = BoundGenericType::get(keyedContainerDecl, Type(), @@ -474,7 +434,7 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl) { // let container : KeyedEncodingContainer auto codingKeysType = codingKeysEnum->getDeclaredType(); auto *containerDecl = createKeyedContainer(C, funcDC, - C.Id_KeyedEncodingContainer, + C.getKeyedEncodingContainerDecl(), codingKeysType, /*isLet=*/false); auto *containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), @@ -616,7 +576,7 @@ static FuncDecl *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl, // Create from the inside out: // (to: Encoder) - auto encoderType = lookupFoundationType(C, C.Id_Encoder); + auto encoderType = C.getEncoderDecl()->getDeclaredInterfaceType(); auto inputTypeElt = TupleTypeElt(encoderType, C.Id_to); auto inputType = TupleType::get(ArrayRef(inputTypeElt), C); @@ -726,7 +686,7 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl) { // let container : KeyedDecodingContainer auto codingKeysType = codingKeysEnum->getDeclaredType(); auto *containerDecl = createKeyedContainer(C, funcDC, - C.Id_KeyedEncodingContainer, + C.getKeyedDecodingContainerDecl(), codingKeysType, /*isLet=*/true); auto *containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), @@ -885,7 +845,7 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl, // Compute from the inside out: // (from: Decoder) - auto decoderType = lookupFoundationType(C, C.Id_Decoder); + auto decoderType = C.getDecoderDecl()->getDeclaredInterfaceType(); auto inputTypeElt = TupleTypeElt(decoderType, C.Id_from); auto inputType = TupleType::get(ArrayRef(inputTypeElt), C); diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp index dbda2193c0601..fde6f0d50a081 100644 --- a/lib/Sema/DerivedConformanceCodingKey.cpp +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// // -// This file implements explicit derivation of the Foundation CodingKey -// protocol for an enum. +// This file implements explicit derivation of the CodingKey protocol for an +// enum. // //===----------------------------------------------------------------------===//