diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 68dea061dced5..854f00e97ea69 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2912,6 +2912,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; }; @@ -2921,8 +2924,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. @@ -6119,7 +6123,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 3e4885c503884..232133b949fab 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1966,6 +1966,12 @@ ERROR(broken_int_integer_literal_convertible_conformance,none, "Int type is broken: does not conform to ExpressibleByIntegerLiteral", ()) 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_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 71fcbb58e410f..12d54ddb3a285 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -29,24 +29,40 @@ IDENTIFIER(Any) IDENTIFIER(atIndexedSubscript) IDENTIFIER_(bridgeToObjectiveC) IDENTIFIER_WITH_NAME(code_, "_code") +IDENTIFIER(CodingKeys) +IDENTIFIER(container) IDENTIFIER(CoreGraphics) IDENTIFIER(CoreMedia) 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) 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(keyPath) IDENTIFIER(makeIterator) IDENTIFIER(Iterator) @@ -70,10 +86,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 c316f62c9e00f..a69edc09cb4a3 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -60,6 +60,9 @@ PROTOCOL_(ErrorCodeProtocol) PROTOCOL(OptionSet) PROTOCOL_(BridgedNSError) PROTOCOL_(BridgedStoredNSError) +PROTOCOL(CodingKey) +PROTOCOL(Encodable) +PROTOCOL(Decodable) PROTOCOL_(ObjectiveCBridgeable) PROTOCOL_(DestructorSafeContainer) diff --git a/include/swift/AST/KnownStdlibTypes.def b/include/swift/AST/KnownStdlibTypes.def index a8a4b4c95680b..b57099b72285b 100644 --- a/include/swift/AST/KnownStdlibTypes.def +++ b/include/swift/AST/KnownStdlibTypes.def @@ -71,4 +71,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/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 8bb1c2b70d4d5..895e043dc81da 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -203,7 +203,8 @@ struct SynthesizedExtensionAnalyzer::Implementation { unsigned countInherits(ExtensionDecl *ED) { unsigned Count = 0; for (auto TL : ED->getInherited()) { - if (shouldPrint(TL.getType()->getAnyNominal(), Options)) + auto *nominal = TL.getType()->getAnyNominal(); + if (nominal && shouldPrint(nominal, Options)) Count ++; } return Count; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 049a01b1a7adc..a253b13ca18c1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2106,9 +2106,42 @@ bool NominalTypeDecl::derivesProtocolConformance(ProtocolDecl *protocol) const { return isObjC() && enumDecl->hasCases() && 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(); + } + + // hasOnlyCasesWithoutAssociatedValues will return true for empty enums; + // empty enumas are allowed to conform as well. + return enumDecl->hasOnlyCasesWithoutAssociatedValues(); + } + default: return false; } + } else if (isa(this) || isa(this)) { + // 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 + // 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 5567fb43113d5..ce8e24490f5ce 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5538,6 +5538,9 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::ErrorCodeProtocol: case KnownProtocolKind::ExpressibleByBuiltinConstStringLiteral: case KnownProtocolKind::ExpressibleByBuiltinConstUTF16StringLiteral: + case KnownProtocolKind::CodingKey: + case KnownProtocolKind::Encodable: + case KnownProtocolKind::Decodable: return SpecialProtocol::None; } diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index beea40d28fdca..101a87b110e9a 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..214ec5a74d3ba --- /dev/null +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -0,0 +1,1022 @@ +//===--- 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 Encodable and Decodable +// protocols for a struct or class. +// +//===----------------------------------------------------------------------===// +// +#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 conforms to the given protocol. +/// +/// \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 *superclassDecl = type->getSuperclassDecl(); + auto *superclassModule = superclassDecl->getModuleContext(); + return (bool)superclassModule->lookupConformance(type->getSuperclass(), + 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)); +} + +/// Validates that all the variables declared in the given list of declarations +/// 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 {En,Decodable} conformance. +/// +/// \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 +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 + // 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(), proto, 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. +/// +/// \param tc The typechecker to use in validating {En,Decodable} conformance. +/// +/// \param codingKeysDecl The \c CodingKeys enum decl to validate. +/// +/// \param type The nominal type decl to validate the \c 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, ProtocolDecl *proto) { + // 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::SmallDenseSet names; + + auto storedProperties = type->getStoredProperties(/*skipInaccessible=*/true); + auto validVarCallback = [&names](VarDecl *varDecl) { + names.insert(varDecl->getName()); + }; + + if (!validateVarsConformToProtocol(tc, type->getDeclContext(), + storedProperties, proto, 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(); +} + +/// Returns whether the given type has a valid nested \c CodingKeys enum. +/// +/// 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 {En,Decodable} conformance. +/// +/// \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, + ProtocolDecl *proto) { + auto &C = tc.Context; + auto codingKeysDecls = type->lookupDirect(DeclName(C.Id_CodingKeys)); + 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}; + } + + // 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}; + } + + // 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); + + codingKeysEnum = dyn_cast(codingKeysType->getAnyNominal()); + } + + if (!codingKeysEnum) { + // TODO: Produce a diagnostic complaining that we cannot derive Codable + // with a non-enum CodingKeys type. + return {/* has type? */ true, /* error? */ true}; + } + + bool valid = validateCodingKeysEnum(tc, codingKeysEnum, type, proto); + return {/* has type? */ true, /* error? */ !valid}; +} + +/// 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 {En,De}codable conformance. +/// +/// \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, + 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 *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(), + inherited, nullptr, typeDC); + enumDecl->setImplicit(); + enumDecl->setAccessibility(Accessibility::Private); + + auto *enumDC = cast(enumDecl); + auto *mutableEnumDC = cast(enumDecl); + + // 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 && + (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. + 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 {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(), + TypeLoc(), /*HasArgumentType=*/false, + SourceLoc(), nullptr, enumDC); + elt->setImplicit(); + mutableEnumDC->addMember(elt); + }; + + if (!validateVarsConformToProtocol(tc, type->getDeclContext(), + storedProperties, proto, validVarCallback)) + return nullptr; + + // Forcibly derive conformance to CodingKey. + tc.checkConformancesInContext(enumDC, mutableEnumDC); + + // Add to the type. + cast(type)->addMember(enumDecl); + return enumDecl; +} + +/// Creates a new var decl representing +/// +/// var/let container : containerBase +/// +/// \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 \c DeclContext to create the decl 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, + NominalTypeDecl *keyedContainerDecl, + Type keyType, bool isLet) { + // 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=*/isLet, + /*IsCaptureList=*/false, SourceLoc(), + C.Id_container, containerType, DC); + containerDecl->setImplicit(); + containerDecl->setInterfaceType(containerType); + return containerDecl; +} + +/// Creates a new \c CallExpr representing +/// +/// base.container(keyedBy: CodingKeys.self) +/// +/// \param C The AST context to create the expression in. +/// +/// \param DC The \c 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); + 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)); +} + +/// Synthesizes the body for `func encode(to encoder: Encoder) throws`. +/// +/// \param encodeDecl The function decl whose body to synthesize. +static void deriveBodyEncodable_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 { + // var 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); + auto &C = funcDC->getASTContext(); + + // We'll want the CodingKeys enum for this type. + 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) 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.getKeyedEncodingContainerDecl(), + codingKeysType, /*isLet=*/false); + + auto *containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), + DeclNameLoc(), /*Implicit=*/true, + AccessSemantics::DirectToStorage); + + // Need to generate + // `let container = encoder.container(keyedBy: CodingKeys.self)` + // This is unconditional because a type with no properties should encode as an + // empty container. + // + // `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 : codingKeysEnum->getAllElements()) { + // 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 (classDecl && superclassIsEncodable(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); +} + +/// 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 *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl, + NominalTypeDecl *type) { + auto &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 = C.getEncoderDecl()->getDeclaredInterfaceType(); + 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(deriveBodyEncodable_encode); + + // This method should be marked as 'override' for classes inheriting Encodable + // conformance from a parent class. + auto *classDecl = dyn_cast(type); + if (classDecl && superclassIsEncodable(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; +} + +/// Synthesizes the body for `init(from decoder: Decoder) throws`. +/// +/// \param initDecl The function decl whose body to synthesize. +static void deriveBodyDecodable_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); + auto &C = funcDC->getASTContext(); + + // We'll want the CodingKeys enum for this type. + 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) 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.getKeyedDecodingContainerDecl(), + codingKeysType, /*isLet=*/true); + + 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) { + // 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); + 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 Decodable should decode super as well. + auto *classDecl = dyn_cast(typeDecl); + if (classDecl && superclassIsDecodable(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); +} + +/// 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 *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl, + NominalTypeDecl *type) { + auto &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 = C.getDecoderDecl()->getDeclaredInterfaceType(); + 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(deriveBodyDecodable_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; +} + +/// Returns whether the given type is valid for synthesizing {En,De}codable. +/// +/// 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 typechecker to use in validating {En,Decodable} conformance. +/// +/// \param type The type to validate. +/// +/// \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, proto); + + // We found a type, but it wasn't valid. + if (error) + return false; + + // We can try to synthesize a type here. + if (!hasType) { + auto *synthesizedEnum = synthesizeCodingKeysEnum(tc, type, proto); + if (!synthesizedEnum) + return false; + } + + return true; +} + +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. + 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; +} + +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/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp new file mode 100644 index 0000000000000..fde6f0d50a081 --- /dev/null +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -0,0 +1,561 @@ +//===--- 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 CodingKey protocol for an +// enum. +// +//===----------------------------------------------------------------------===// + +#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; + +/// 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); +} + +/// 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); +} + +/// 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(); + + // 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, + valueParam->getType(), parentDC); + rawValueDecl->setInterfaceType(C.getIntDecl()->getDeclaredType()); + rawValueDecl->setImplicit(); + auto *paramList = ParameterList::createWithoutLoc(rawValueDecl); + + // init(rawValue:) constructor name + DeclName ctorName(C, C.Id_init, paramList); + + // self.init(rawValue:) expr + 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); +} + +/// 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(); + + // 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 *initDecl = + new (C) ConstructorDecl(name, SourceLoc(), + /*Failability=*/OTK_Optional, + /*FailabilityLoc=*/SourceLoc(), + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + selfDecl, paramList, + /*GenericParams=*/nullptr, parentDC); + + initDecl->setImplicit(); + + // Synthesize the body. + synthesizer(initDecl); + + // Compute the type of the initializer. + 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 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; +} + +/// 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 = strValDecl->getDeclContext(); + auto &C = parentDC->getASTContext(); + + auto *enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); + Type enumType = parentDC->getDeclaredTypeInContext(); + + BraceStmt *body = nullptr; + auto elements = enumDecl->getAllElements(); + if (elements.empty() /* empty enum */) { + // 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 *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 *selfRef = createSelfDeclRef(strValDecl); + auto *switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), + selfRef, SourceLoc(), cases, + SourceLoc(), C); + 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_init_stringValue(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(); + auto &C = parentDC->getASTContext(); + + auto *enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); + Type enumType = parentDC->getDeclaredTypeInContext(); + + auto elements = enumDecl->getAllElements(); + if (elements.empty() /* empty enum */) { + deriveNilReturn(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); +} + +/// 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); + rawType = parentDC->mapTypeIntoContext(rawType); + + auto &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 &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 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, stringType, + 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; +} diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index ba88b6b0aea37..af32b3850a33d 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -59,6 +59,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; } @@ -67,14 +75,33 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, if (func->isOperator() && name.getBaseName().str() == "==") return getRequirement(KnownProtocolKind::Equatable); + // 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::Encodable); + } + 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); + + // Decodable.init(from: Decoder) + if (argumentNames[0] == ctx.Id_from) + return getRequirement(KnownProtocolKind::Decodable); + } return nullptr; } diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 6cfe2e3bad08e..9b56a26890bfe 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -96,6 +96,30 @@ 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 an Encodable requirement for a struct type. +/// +/// \returns the derived member, which will also be added to the type. +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, Decl *parentDecl, diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index c2f1ee41fd578..efce518332723 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -6217,6 +6217,15 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, return DerivedConformance::deriveBridgedNSError(*this, Decl, TypeDecl, Requirement); + case KnownProtocolKind::CodingKey: + return DerivedConformance::deriveCodingKey(*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; } diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index d19bd2402925f..edba3b0f191a4 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -100,6 +100,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 9deb3bfc63333..aa08242a42f80 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -790,6 +790,7 @@ class TypeChecker final : public LazyResolver { Type ImageLiteralType; Type FileReferenceLiteralType; Type StringType; + Type IntType; Type Int8Type; Type UInt8Type; Type NSObjectType; @@ -880,6 +881,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/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index b2d332d9c16b5..06ffb8e582909 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -35,6 +35,7 @@ set(SWIFTLIB_ESSENTIAL BuiltinMath.swift.gyb Character.swift CocoaArray.swift + Codable.swift Collection.swift CollectionAlgorithms.swift.gyb Comparable.swift diff --git a/stdlib/public/core/Codable.swift b/stdlib/public/core/Codable.swift new file mode 100644 index 0000000000000..38f0991ea83bf --- /dev/null +++ b/stdlib/public/core/Codable.swift @@ -0,0 +1,3176 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Codable +//===----------------------------------------------------------------------===// + +/// Conformance to `Encodable` indicates that a type can encode itself to an external representation. +public protocol Encodable { + /// Encodes `self` into the given encoder. + /// + /// If `self` fails to encode anything, `encoder` will encode an empty keyed container in its place. + /// + /// - parameter encoder: The encoder to write data to. + /// - throws: An error if any values are invalid for `encoder`'s format. + func encode(to encoder: Encoder) throws +} + +/// Conformance to `Decodable` indicates that a type can decode itself from an external representation. +public protocol Decodable { + /// Initializes `self` by decoding from `decoder`. + /// + /// - parameter decoder: The decoder to read data from. + /// - throws: An error if reading from the decoder fails, or if read data is corrupted or otherwise invalid. + init(from decoder: Decoder) throws +} + +/// Conformance to `Codable` indicates that a type can convert itself into and out of an external representation. +public typealias Codable = Encodable & Decodable + +//===----------------------------------------------------------------------===// +// CodingKey +//===----------------------------------------------------------------------===// + +/// Conformance to `CodingKey` indicates that a type can be used as a key for encoding and decoding. +public protocol CodingKey { + /// The string to use in a named collection (e.g. a string-keyed dictionary). + var stringValue: String { get } + + /// Initializes `self` from a string. + /// + /// - parameter stringValue: The string value of the desired key. + /// - returns: An instance of `Self` from the given string, or `nil` if the given string does not correspond to any instance of `Self`. + init?(stringValue: String) + + /// The int to use in an indexed collection (e.g. an int-keyed dictionary). + var intValue: Int? { get } + + /// Initializes `self` from an integer. + /// + /// - parameter intValue: The integer value of the desired key. + /// - returns: An instance of `Self` from the given integer, or `nil` if the given integer does not correspond to any instance of `Self`. + init?(intValue: Int) +} + +//===----------------------------------------------------------------------===// +// Encoder & Decoder +//===----------------------------------------------------------------------===// + +/// An `Encoder` is a type which can encode values into a native format for external representation. +public protocol Encoder { + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Any contextual information set by the user for encoding. + var userInfo: [CodingUserInfoKey : Any] { get } + + /// Returns an encoding container appropriate for holding multiple values keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A new keyed encoding container. + /// - precondition: May not be called after a prior `self.unkeyedContainer()` call. + /// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call. + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer + + /// Returns an encoding container appropriate for holding multiple unkeyed values. + /// + /// - returns: A new empty unkeyed container. + /// - precondition: May not be called after a prior `self.container(keyedBy:)` call. + /// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call. + func unkeyedContainer() -> UnkeyedEncodingContainer + + /// Returns an encoding container appropriate for holding a single primitive value. + /// + /// - returns: A new empty single value container. + /// - precondition: May not be called after a prior `self.container(keyedBy:)` call. + /// - precondition: May not be called after a prior `self.unkeyedContainer()` call. + /// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call. + func singleValueContainer() -> SingleValueEncodingContainer +} + +/// A `Decoder` is a type which can decode values from a native format into in-memory representations. +public protocol Decoder { + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Any contextual information set by the user for decoding. + var userInfo: [CodingUserInfoKey : Any] { get } + + /// Returns the data stored in `self` as represented in a container keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is not a keyed container. + func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer + + /// Returns the data stored in `self` as represented in a container appropriate for holding values with no keys. + /// + /// - returns: An unkeyed container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is not an unkeyed container. + func unkeyedContainer() throws -> UnkeyedDecodingContainer + + /// Returns the data stored in `self` as represented in a container appropriate for holding a single primitive value. + /// + /// - returns: A single value container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is not a single value container. + func singleValueContainer() throws -> SingleValueDecodingContainer +} + +//===----------------------------------------------------------------------===// +// Keyed Encoding Containers +//===----------------------------------------------------------------------===// + +/// Conformance to `KeyedEncodingContainerProtocol` indicates that a type provides a view into an `Encoder`'s storage and is used to hold the encoded properties of an `Encodable` type in a keyed manner. +/// +/// Encoders should provide types conforming to `KeyedEncodingContainerProtocol` for their format. +public protocol KeyedEncodingContainerProtocol { + associatedtype Key : CodingKey + + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: T?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Bool?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int8?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int16?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int32?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int64?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt8?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt16?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt32?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt64?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Float?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Double?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: String?, forKey key: Key) throws + + /// Encodes the given object weakly for the given key. + /// + /// For `Encoder`s that implement this functionality, this will only encode the given object and associate it with the given key if it is encoded unconditionally elsewhere in the payload (either previously or in the future). + /// + /// For formats which don't support this feature, the default implementation encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - parameter key: The key to associate the object with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encodeWeak(_ object: T?, forKey key: Key) throws + + /// Stores a keyed encoding container for the given key and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - parameter key: The key to encode the container for. + /// - returns: A new keyed encoding container. + mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer + + /// Stores an unkeyed encoding container for the given key and returns it. + /// + /// - parameter key: The key to encode the container for. + /// - returns: A new unkeyed encoding container. + mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer + + /// Stores a new nested container for the default `super` key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// Equivalent to calling `superEncoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder() -> Encoder + + /// Stores a new nested container for the given key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// - parameter key: The key to encode `super` for. + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder(forKey key: Key) -> Encoder +} + +// An implementation of _KeyedEncodingContainerBase and _KeyedEncodingContainerBox are given at the bottom of this file. +/// `KeyedEncodingContainer` is a type-erased box for `KeyedEncodingContainerProtocol` types, similar to `AnyCollection` and `AnyHashable`. This is the type which consumers of the API interact with directly. +public struct KeyedEncodingContainer { + public typealias Key = K + + /// The container for the concrete encoder. The type is _*Base so that it's generic on the key type. + private var box: _KeyedEncodingContainerBase + + /// Initializes `self` with the given container. + /// + /// - parameter container: The container to hold. + public init(_ container: Container) where Container.Key == Key { + box = _KeyedEncodingContainerBox(container) + } + + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + public var codingPath: [CodingKey?] { + return box.codingPath + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: Bool?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: Int?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: Int8?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: Int16?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: Int32?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: Int64?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: UInt?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: UInt8?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: UInt16?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: UInt32?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: UInt64?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: Float?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: Double?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: String?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encode(_ value: T?, forKey key: Key) throws { + try box.encode(value, forKey: key) + } + + /// Encodes the given object weakly for the given key. + /// + /// For `Encoder`s that implement this functionality, this will only encode the given object and associate it with the given key if it is encoded unconditionally elsewhere in the payload (either previously or in the future). + /// + /// - parameter object: The object to encode. + /// - parameter key: The key to associate the object with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + public mutating func encodeWeak(_ object: T?, forKey key: Key) throws { + try box.encodeWeak(object, forKey: key) + } + + /// Stores a keyed encoding container for the given key and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - parameter key: The key to encode the container for. + /// - returns: A new keyed encoding container. + public mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { + return box.nestedContainer(keyedBy: NestedKey.self, forKey: key) + } + + /// Stores an unkeyed encoding container for the given key and returns it. + /// + /// - parameter key: The key to encode the container for. + /// - returns: A new unkeyed encoding container. + public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + return box.nestedUnkeyedContainer(forKey: key) + } + + /// Stores a new nested container for the default `super` key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// Equivalent to calling `superEncoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + public mutating func superEncoder() -> Encoder { + return box.superEncoder() + } + + /// Stores a new nested container for the given key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// - parameter key: The key to encode `super` for. + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + public mutating func superEncoder(forKey key: Key) -> Encoder { + return box.superEncoder(forKey: key) + } +} + +/// Conformance to `KeyedDecodingContainerProtocol` indicates that a type provides a view into a `Decoder`'s storage and is used to hold the encoded properties of a `Decodable` type in a keyed manner. +/// +/// Decoders should provide types conforming to `UnkeyedDecodingContainer` for their format. +public protocol KeyedDecodingContainerProtocol { + associatedtype Key : CodingKey + + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// All the keys the `Decoder` has for this container. + /// + /// Different keyed containers from the same `Decoder` may return different keys here; it is possible to encode with multiple key types which are not convertible to one another. This should report all keys present which are convertible to the requested type. + var allKeys: [Key] { get } + + /// Returns whether the `Decoder` contains a value associated with the given key. + /// + /// The value associated with the given key may be a null value as appropriate for the data format. + /// + /// - parameter key: The key to search for. + /// - returns: Whether the `Decoder` has an entry for the given key. + func contains(_ key: Key) -> Bool + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: Int.Type, forKey key: Key) throws -> Int + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: Float.Type, forKey key: Key) throws -> Float + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: Double.Type, forKey key: Key) throws -> Double + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: String.Type, forKey key: Key) throws -> String + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func decode(_ type: T.Type, forKey key: Key) throws -> T + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: T.Type, forKey key: Key) throws -> T? + + /// Returns the data stored for the given key as represented in a container keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - parameter key: The key that the nested container is associated with. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is not a keyed container. + func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer + + /// Returns the data stored for the given key as represented in an unkeyed container. + /// + /// - parameter key: The key that the nested container is associated with. + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is not an unkeyed container. + func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer + + /// Returns a `Decoder` instance for decoding `super` from the container associated with the default `super` key. + /// + /// Equivalent to calling `superDecoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the default `super` key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the default `super` key. + func superDecoder() throws -> Decoder + + /// Returns a `Decoder` instance for decoding `super` from the container associated with the given key. + /// + /// - parameter key: The key to decode `super` for. + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + func superDecoder(forKey key: Key) throws -> Decoder +} + +// An implementation of _KeyedDecodingContainerBase and _KeyedDecodingContainerBox are given at the bottom of this file. +/// `KeyedDecodingContainer` is a type-erased box for `KeyedDecodingContainerProtocol` types, similar to `AnyCollection` and `AnyHashable`. This is the type which consumers of the API interact with directly. +public struct KeyedDecodingContainer : KeyedDecodingContainerProtocol { + public typealias Key = K + + /// The container for the concrete decoder. The type is _*Base so that it's generic on the key type. + private var box: _KeyedDecodingContainerBase + + /// Initializes `self` with the given container. + /// + /// - parameter container: The container to hold. + public init(_ container: Container) where Container.Key == Key { + box = _KeyedDecodingContainerBox(container) + } + + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + public var codingPath: [CodingKey?] { + return box.codingPath + } + + /// All the keys the `Decoder` has for this container. + /// + /// Different keyed containers from the same `Decoder` may return different keys here; it is possible to encode with multiple key types which are not convertible to one another. This should report all keys present which are convertible to the requested type. + public var allKeys: [Key] { + return box.allKeys + } + + /// Returns whether the `Decoder` contains a value associated with the given key. + /// + /// The value associated with the given key may be a null value as appropriate for the data format. + /// + /// - parameter key: The key to search for. + /// - returns: Whether the `Decoder` has an entry for the given key. + public func contains(_ key: Key) -> Bool { + return box.contains(key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? { + return try box.decodeIfPresent(Bool.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? { + return try box.decodeIfPresent(Int.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? { + return try box.decodeIfPresent(Int8.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? { + return try box.decodeIfPresent(Int16.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? { + return try box.decodeIfPresent(Int32.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? { + return try box.decodeIfPresent(Int64.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? { + return try box.decodeIfPresent(UInt.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? { + return try box.decodeIfPresent(UInt8.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? { + return try box.decodeIfPresent(UInt16.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? { + return try box.decodeIfPresent(UInt32.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? { + return try box.decodeIfPresent(UInt64.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? { + return try box.decodeIfPresent(Float.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? { + return try box.decodeIfPresent(Double.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? { + return try box.decodeIfPresent(String.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + public func decodeIfPresent(_ type: T.Type, forKey key: Key) throws -> T? { + return try box.decodeIfPresent(T.self, forKey: key) + } + + /// Returns the data stored for the given key as represented in a container keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - parameter key: The key that the nested container is associated with. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is not a keyed container. + public func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer { + return try box.nestedContainer(keyedBy: NestedKey.self, forKey: key) + } + + /// Returns the data stored for the given key as represented in an unkeyed container. + /// + /// - parameter key: The key that the nested container is associated with. + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is not an unkeyed container. + public func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { + return try box.nestedUnkeyedContainer(forKey: key) + } + + /// Returns a `Decoder` instance for decoding `super` from the container associated with the default `super` key. + /// + /// Equivalent to calling `superDecoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the default `super` key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the default `super` key. + public func superDecoder() throws -> Decoder { + return try box.superDecoder() + } + + /// Returns a `Decoder` instance for decoding `super` from the container associated with the given key. + /// + /// - parameter key: The key to decode `super` for. + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for the given key. + public func superDecoder(forKey key: Key) throws -> Decoder { + return try box.superDecoder(forKey: key) + } +} + +//===----------------------------------------------------------------------===// +// Unkeyed Encoding Containers +//===----------------------------------------------------------------------===// + +/// Conformance to `UnkeyedEncodingContainer` indicates that a type provides a view into an `Encoder`'s storage and is used to hold the encoded properties of an `Encodable` type sequentially, without keys. +/// +/// Encoders should provide types conforming to `UnkeyedEncodingContainer` for their format. +public protocol UnkeyedEncodingContainer { + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: T?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Bool?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int8?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int16?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int32?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Int64?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt8?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt16?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt32?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: UInt64?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Float?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Double?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: String?) throws + + /// Encodes the given object weakly. + /// + /// For `Encoder`s that implement this functionality, this will only encode the given object if it is encoded unconditionally elsewhere in the payload (either previously or in the future). + /// + /// For formats which don't support this feature, the default implementation encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + mutating func encodeWeak(_ object: T?) throws + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Bool + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int8 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int16 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int32 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int64 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt8 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt16 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt32 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt64 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Float + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Double + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == String + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element : Encodable + + /// Encodes a nested container keyed by the given type and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - returns: A new keyed encoding container. + mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer + + /// Encodes an unkeyed encoding container and returns it. + /// + /// - returns: A new unkeyed encoding container. + mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer + + /// Encodes a nested container and returns an `Encoder` instance for encoding `super` into that container. + /// + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder() -> Encoder +} + +/// Conformance to `UnkeyedDecodingContainer` indicates that a type provides a view into a `Decoder`'s storage and is used to hold the encoded properties of a `Decodable` type sequentially, without keys. +/// +/// Decoders should provide types conforming to `UnkeyedDecodingContainer` for their format. +public protocol UnkeyedDecodingContainer { + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Returns the number of elements (if known) contained within this container. + var count: Int? { get } + + /// Returns whether there are no more elements left to be decoded in the container. + var isAtEnd: Bool { get } + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Bool.Type) throws -> Bool + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Int.Type) throws -> Int + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Int8.Type) throws -> Int8 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Int16.Type) throws -> Int16 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Int32.Type) throws -> Int32 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Int64.Type) throws -> Int64 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: UInt.Type) throws -> UInt + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: UInt8.Type) throws -> UInt8 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: UInt16.Type) throws -> UInt16 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: UInt32.Type) throws -> UInt32 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: UInt64.Type) throws -> UInt64 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Float.Type) throws -> Float + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Double.Type) throws -> Double + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: String.Type) throws -> String + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: T.Type) throws -> T + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int.Type) throws -> Int? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int8.Type) throws -> Int8? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int16.Type) throws -> Int16? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int32.Type) throws -> Int32? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int64.Type) throws -> Int64? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt.Type) throws -> UInt? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt8.Type) throws -> UInt8? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt16.Type) throws -> UInt16? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt32.Type) throws -> UInt32? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt64.Type) throws -> UInt64? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Float.Type) throws -> Float? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Double.Type) throws -> Double? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: String.Type) throws -> String? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: T.Type) throws -> T? + + /// Decodes a nested container keyed by the given type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is not a keyed container. + mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer + + /// Decodes an unkeyed nested container. + /// + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is not an unkeyed container. + mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer + + /// Decodes a nested container and returns a `Decoder` instance for decoding `super` from that container. + /// + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func superDecoder() throws -> Decoder +} + +//===----------------------------------------------------------------------===// +// Single Value Encoding Containers +//===----------------------------------------------------------------------===// + +/// A `SingleValueEncodingContainer` is a container which can support the storage and direct encoding of a single non-keyed value. +public protocol SingleValueEncodingContainer { + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Bool) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Int) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Int8) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Int16) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Int32) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Int64) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: UInt) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: UInt8) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: UInt16) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: UInt32) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: UInt64) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Float) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Double) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: String) throws +} + +/// A `SingleValueDecodingContainer` is a container which can support the storage and direct decoding of a single non-keyed value. +public protocol SingleValueDecodingContainer { + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Bool.Type) throws -> Bool + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Int.Type) throws -> Int + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Int8.Type) throws -> Int8 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Int16.Type) throws -> Int16 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Int32.Type) throws -> Int32 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Int64.Type) throws -> Int64 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: UInt.Type) throws -> UInt + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: UInt8.Type) throws -> UInt8 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: UInt16.Type) throws -> UInt16 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: UInt32.Type) throws -> UInt32 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: UInt64.Type) throws -> UInt64 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Float.Type) throws -> Float + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Double.Type) throws -> Double + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: String.Type) throws -> String +} + +//===----------------------------------------------------------------------===// +// User Info +//===----------------------------------------------------------------------===// + +/// Represents a user-defined key for providing context for encoding and decoding. +public struct CodingUserInfoKey : RawRepresentable, Equatable, Hashable { + public typealias RawValue = String + + /// The key's string value. + public let rawValue: String + + /// Initializes `self` with the given raw value. + /// + /// - parameter rawValue: The value of the key. + public init?(rawValue: String) { + self.rawValue = rawValue + } + + /// Returns whether the given keys are equal. + /// + /// - parameter lhs: The key to compare against. + /// - parameter rhs: The key to compare with. + public static func ==(_ lhs: CodingUserInfoKey, _ rhs: CodingUserInfoKey) -> Bool { + return lhs.rawValue == rhs.rawValue + } + + /// The key's hash value. + public var hashValue: Int { + return self.rawValue.hashValue + } +} + +//===----------------------------------------------------------------------===// +// Errors +//===----------------------------------------------------------------------===// + +/// An `EncodingError` indicates that something has gone wrong during encoding of a value. +public enum EncodingError : Error { + /// The context in which the error occurred. + public struct Context { + /// The path of `CodingKey`s taken to get to the point of the failing encode call. + public let codingPath: [CodingKey?] + + /// A description of what went wrong, for debugging purposes. + public let debugDescription: String + + /// Initializes `self` with the given path of `CodingKey`s and a description of what went wrong. + /// + /// - parameter codingPath: The path of `CodingKey`s taken to get to the point of the failing encode call. + /// - parameter debugDescription: A description of what went wrong, for debugging purposes. + public init(codingPath: [CodingKey?], debugDescription: String) { + self.codingPath = codingPath + self.debugDescription = debugDescription + } + } + + /// `.invalidValue` indicates that an `Encoder` or its containers could not encode the given value. + /// + /// Contains the attempted value, along with context for debugging. + case invalidValue(Any, Context) +} + +/// A `DecodingError` indicates that something has gone wrong during decoding of a value. +public enum DecodingError : Error { + /// The context in which the error occurred. + public struct Context { + /// The path of `CodingKey`s taken to get to the point of the failing decode call. + public let codingPath: [CodingKey?] + + /// A description of what went wrong, for debugging purposes. + public let debugDescription: String + + /// Initializes `self` with the given path of `CodingKey`s and a description of what went wrong. + /// + /// - parameter codingPath: The path of `CodingKey`s taken to get to the point of the failing decode call. + /// - parameter debugDescription: A description of what went wrong, for debugging purposes. + public init(codingPath: [CodingKey?], debugDescription: String) { + self.codingPath = codingPath + self.debugDescription = debugDescription + } + } + + /// `.typeMismatch` indicates that a value of the given type could not be decoded because it did not match the type of what was found in the encoded payload. + /// + /// Contains the attempted type, along with context for debugging. + case typeMismatch(Any.Type, Context) + + /// `.valueNotFound` indicates that a non-optional value of the given type was expected, but a null value was found. + /// + /// Contains the attempted type, along with context for debugging. + case valueNotFound(Any.Type, Context) + + /// `.keyNotFound` indicates that a `KeyedDecodingContainer` was asked for an entry for the given key, but did not contain one. + /// + /// Contains the attempted key, along with context for debugging. + case keyNotFound(CodingKey, Context) + + /// `.dataCorrupted` indicates that the data is corrupted or otherwise invalid. + /// + /// Contains context for debugging. + case dataCorrupted(Context) +} + +//===----------------------------------------------------------------------===// +// Keyed Encoding Container Implementations +//===----------------------------------------------------------------------===// + +fileprivate class _KeyedEncodingContainerBase { + // These must all be given a concrete implementation in _*Box. + var codingPath: [CodingKey?] { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: Bool?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: Int?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: Int8?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: Int16?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: Int32?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: Int64?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: UInt?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: UInt8?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: UInt16?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: UInt32?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: UInt64?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: Float?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: Double?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: String?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encode(_ value: T?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func encodeWeak(_ object: T?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func superEncoder() -> Encoder { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + func superEncoder(forKey key: Key) -> Encoder { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } +} + +fileprivate final class _KeyedEncodingContainerBox : _KeyedEncodingContainerBase { + typealias Key = Concrete.Key + + var concrete: Concrete + + init(_ container: Concrete) { + concrete = container + } + + override var codingPath: [CodingKey?] { + return concrete.codingPath + } + + override func encode(_ value: T?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: Bool?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: Int?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: Int8?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: Int16?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: Int32?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: Int64?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: UInt?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: UInt8?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: UInt16?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: UInt32?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: UInt64?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: Float?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: Double?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encode(_ value: String?, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override func encodeWeak(_ object: T?, forKey key: Key) throws { + try concrete.encodeWeak(object, forKey: key) + } + + override func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { + return concrete.nestedContainer(keyedBy: NestedKey.self, forKey: key) + } + + override func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + return concrete.nestedUnkeyedContainer(forKey: key) + } + + override func superEncoder() -> Encoder { + return concrete.superEncoder() + } + + override func superEncoder(forKey key: Key) -> Encoder { + return concrete.superEncoder(forKey: key) + } +} + +fileprivate class _KeyedDecodingContainerBase { + var codingPath: [CodingKey?] { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + var allKeys: [Key] { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func contains(_ key: Key) -> Bool { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func decodeIfPresent(_ type: T.Type, forKey key: Key) throws -> T? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func superDecoder() throws -> Decoder { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + func superDecoder(forKey key: Key) throws -> Decoder { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } +} + +fileprivate final class _KeyedDecodingContainerBox : _KeyedDecodingContainerBase { + typealias Key = Concrete.Key + + var concrete: Concrete + + init(_ container: Concrete) { + concrete = container + } + + override var codingPath: [CodingKey?] { + return concrete.codingPath + } + + override var allKeys: [Key] { + return concrete.allKeys + } + + override func contains(_ key: Key) -> Bool { + return concrete.contains(key) + } + + override func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? { + return try concrete.decodeIfPresent(Bool.self, forKey: key) + } + + override func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? { + return try concrete.decodeIfPresent(Int.self, forKey: key) + } + + override func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? { + return try concrete.decodeIfPresent(Int8.self, forKey: key) + } + + override func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? { + return try concrete.decodeIfPresent(Int16.self, forKey: key) + } + + override func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? { + return try concrete.decodeIfPresent(Int32.self, forKey: key) + } + + override func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? { + return try concrete.decodeIfPresent(Int64.self, forKey: key) + } + + override func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? { + return try concrete.decodeIfPresent(UInt.self, forKey: key) + } + + override func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? { + return try concrete.decodeIfPresent(UInt8.self, forKey: key) + } + + override func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? { + return try concrete.decodeIfPresent(UInt16.self, forKey: key) + } + + override func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? { + return try concrete.decodeIfPresent(UInt32.self, forKey: key) + } + + override func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? { + return try concrete.decodeIfPresent(UInt64.self, forKey: key) + } + + override func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? { + return try concrete.decodeIfPresent(Float.self, forKey: key) + } + + override func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? { + return try concrete.decodeIfPresent(Double.self, forKey: key) + } + + override func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? { + return try concrete.decodeIfPresent(String.self, forKey: key) + } + + override func decodeIfPresent(_ type: T.Type, forKey key: Key) throws -> T? { + return try concrete.decodeIfPresent(T.self, forKey: key) + } + + override func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer { + return try concrete.nestedContainer(keyedBy: NestedKey.self, forKey: key) + } + + override func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { + return try concrete.nestedUnkeyedContainer(forKey: key) + } + + override func superDecoder() throws -> Decoder { + return try concrete.superDecoder() + } + + override func superDecoder(forKey key: Key) throws -> Decoder { + return try concrete.superDecoder(forKey: key) + } +} + +//===----------------------------------------------------------------------===// +// Primitive and RawRepresentable Extensions +//===----------------------------------------------------------------------===// + +extension Bool : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Bool.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension Int : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension Int8 : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int8.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension Int16 : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int16.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension Int32 : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int32.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension Int64 : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int64.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension UInt : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension UInt8 : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt8.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension UInt16 : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt16.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension UInt32 : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt32.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension UInt64 : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt64.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension Float : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Float.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension Double : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Double.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension String : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(String.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +public extension RawRepresentable where RawValue == Bool, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == Bool, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == Int, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == Int, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == Int8, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == Int8, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == Int16, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == Int16, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == Int32, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == Int32, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == Int64, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == Int64, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == UInt, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == UInt, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == UInt8, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == UInt8, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == UInt16, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == UInt16, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == UInt32, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == UInt32, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == UInt64, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == UInt64, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == Float, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == Float, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == Double, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == Double, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +public extension RawRepresentable where RawValue == String, Self : Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +public extension RawRepresentable where RawValue == String, Self : Decodable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) + } + + self = value + } +} + +//===----------------------------------------------------------------------===// +// Convenience Default Implementations +//===----------------------------------------------------------------------===// + +// Default implementation for encodeWeak(_:forKey:) in terms of encode(_:forKey:) +public extension KeyedEncodingContainerProtocol { + public mutating func encodeWeak(_ object: T?, forKey key: Key) throws { + try encode(object, forKey: key) + } +} + +// Default implementations for decode(_:forKey:) in terms of decodeIfPresent(_:forKey:) +public extension KeyedDecodingContainerProtocol { + public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { + if let value = try decodeIfPresent(Bool.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: Int.Type, forKey key: Key) throws -> Int { + if let value = try decodeIfPresent(Int.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { + if let value = try decodeIfPresent(Int8.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { + if let value = try decodeIfPresent(Int16.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { + if let value = try decodeIfPresent(Int32.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { + if let value = try decodeIfPresent(Int64.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { + if let value = try decodeIfPresent(UInt.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { + if let value = try decodeIfPresent(UInt8.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { + if let value = try decodeIfPresent(UInt16.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { + if let value = try decodeIfPresent(UInt32.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { + if let value = try decodeIfPresent(UInt64.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: Float.Type, forKey key: Key) throws -> Float { + if let value = try decodeIfPresent(Float.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: Double.Type, forKey key: Key) throws -> Double { + if let value = try decodeIfPresent(Double.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: String.Type, forKey key: Key) throws -> String { + if let value = try decodeIfPresent(String.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } + + public func decode(_ type: T.Type, forKey key: Key) throws -> T { + if let value = try decodeIfPresent(T.self, forKey: key) { + return value + } else if contains(key) { + var path = codingPath + path.append(key) + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: path, debugDescription: "Found null value when expecting non-optional type \(type) for coding key \"\(key)\"")) + } else { + var path = codingPath + path.append(key) + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: path, debugDescription: "Key not found when expecting non-optional type \(type) for coding key \"\(key)\"")) + } + } +} + +// Default implementation of encodeWeak(_:) in terms of encode(_:), and encode(contentsOf:) in terms of encode(_:) loop. +public extension UnkeyedEncodingContainer { + public mutating func encodeWeak(_ object: T?) throws { + try encode(object) + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Bool { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int8 { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int16 { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int32 { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Int64 { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt8 { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt16 { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt32 { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == UInt64 { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Float { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == Double { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element == String { + for element in sequence { + try encode(element) + } + } + + public mutating func encode(contentsOf sequence: T) throws where T.Iterator.Element : Encodable { + for element in sequence { + try encode(element) + } + } +} + +// Default implementations for decode(_:) in terms of decodeIfPresent(_:) +public extension UnkeyedDecodingContainer { + mutating func decode(_ type: Bool.Type) throws -> Bool { + if let value = try decodeIfPresent(Bool.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: Int.Type) throws -> Int { + if let value = try decodeIfPresent(Int.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: Int8.Type) throws -> Int8 { + if let value = try decodeIfPresent(Int8.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: Int16.Type) throws -> Int16 { + if let value = try decodeIfPresent(Int16.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: Int32.Type) throws -> Int32 { + if let value = try decodeIfPresent(Int32.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: Int64.Type) throws -> Int64 { + if let value = try decodeIfPresent(Int64.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: UInt.Type) throws -> UInt { + if let value = try decodeIfPresent(UInt.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: UInt8.Type) throws -> UInt8 { + if let value = try decodeIfPresent(UInt8.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: UInt16.Type) throws -> UInt16 { + if let value = try decodeIfPresent(UInt16.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: UInt32.Type) throws -> UInt32 { + if let value = try decodeIfPresent(UInt32.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: UInt64.Type) throws -> UInt64 { + if let value = try decodeIfPresent(UInt64.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: Float.Type) throws -> Float { + if let value = try decodeIfPresent(Float.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: Double.Type) throws -> Double { + if let value = try decodeIfPresent(Double.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: String.Type) throws -> String { + if let value = try decodeIfPresent(String.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } + + mutating func decode(_ type: T.Type) throws -> T { + if let value = try decodeIfPresent(T.self) { + return value + } else if !isAtEnd { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "Found null value when expecting non-optional type \(type)")) + } else { + throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: codingPath, debugDescription: "No remaining elements when expecting non-optional type \(type)")) + } + } +} diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index 6555ed5851374..4a78c5221532b 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -162,6 +162,7 @@ "DebuggerSupport.swift", "Equatable.swift", "Comparable.swift", - "Hashable.swift" + "Hashable.swift", + "Codable.swift" ] } diff --git a/test/SourceKit/DocSupport/doc_clang_module.swift.response b/test/SourceKit/DocSupport/doc_clang_module.swift.response index 25ed2edb7f533..bb9fcdc904b1f 100644 --- a/test/SourceKit/DocSupport/doc_clang_module.swift.response +++ b/test/SourceKit/DocSupport/doc_clang_module.swift.response @@ -5259,6 +5259,11 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.length: 31, key.fully_annotated_decl: "init(rawValue: UInt32)", key.conforms: [ + { + key.kind: source.lang.swift.ref.function.constructor, + key.name: "init(rawValue:)", + key.usr: "s:s16RawRepresentablePxSg0A5ValueQz03rawC0_tcfc" + }, { key.kind: source.lang.swift.ref.function.constructor, key.name: "init(rawValue:)", @@ -5283,6 +5288,11 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.length: 20, key.fully_annotated_decl: "var rawValue: UInt32", key.conforms: [ + { + key.kind: source.lang.swift.ref.var.instance, + key.name: "rawValue", + key.usr: "s:s16RawRepresentableP8rawValue0aD0Qzv" + }, { key.kind: source.lang.swift.ref.var.instance, key.name: "rawValue", @@ -5372,6 +5382,11 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.length: 31, key.fully_annotated_decl: "init(rawValue: UInt32)", key.conforms: [ + { + key.kind: source.lang.swift.ref.function.constructor, + key.name: "init(rawValue:)", + key.usr: "s:s16RawRepresentablePxSg0A5ValueQz03rawC0_tcfc" + }, { key.kind: source.lang.swift.ref.function.constructor, key.name: "init(rawValue:)", @@ -5396,6 +5411,11 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.length: 20, key.fully_annotated_decl: "var rawValue: UInt32", key.conforms: [ + { + key.kind: source.lang.swift.ref.var.instance, + key.name: "rawValue", + key.usr: "s:s16RawRepresentableP8rawValue0aD0Qzv" + }, { key.kind: source.lang.swift.ref.var.instance, key.name: "rawValue", @@ -5492,6 +5512,11 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.length: 31, key.fully_annotated_decl: "init(rawValue: UInt32)", key.conforms: [ + { + key.kind: source.lang.swift.ref.function.constructor, + key.name: "init(rawValue:)", + key.usr: "s:s16RawRepresentablePxSg0A5ValueQz03rawC0_tcfc" + }, { key.kind: source.lang.swift.ref.function.constructor, key.name: "init(rawValue:)", @@ -5516,6 +5541,11 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.length: 20, key.fully_annotated_decl: "var rawValue: UInt32", key.conforms: [ + { + key.kind: source.lang.swift.ref.var.instance, + key.name: "rawValue", + key.usr: "s:s16RawRepresentableP8rawValue0aD0Qzv" + }, { key.kind: source.lang.swift.ref.var.instance, key.name: "rawValue", @@ -8017,6 +8047,11 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.length: 31, key.fully_annotated_decl: "init(rawValue: UInt32)", key.conforms: [ + { + key.kind: source.lang.swift.ref.function.constructor, + key.name: "init(rawValue:)", + key.usr: "s:s16RawRepresentablePxSg0A5ValueQz03rawC0_tcfc" + }, { key.kind: source.lang.swift.ref.function.constructor, key.name: "init(rawValue:)", @@ -8041,6 +8076,11 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.length: 20, key.fully_annotated_decl: "var rawValue: UInt32", key.conforms: [ + { + key.kind: source.lang.swift.ref.var.instance, + key.name: "rawValue", + key.usr: "s:s16RawRepresentableP8rawValue0aD0Qzv" + }, { key.kind: source.lang.swift.ref.var.instance, key.name: "rawValue", 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..5705df01b375e --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/class_codable_simple_multi1.swift @@ -0,0 +1,21 @@ +// 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..db56682e765c5 --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/enum_coding_key_extension_multi2.swift @@ -0,0 +1,3 @@ +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..067e069294893 --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/enum_coding_key_multi1.swift @@ -0,0 +1,11 @@ +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..7d72e19fe049d --- /dev/null +++ b/test/decl/protocol/special/coding/Inputs/struct_codable_simple_multi1.swift @@ -0,0 +1,23 @@ +// RUN: %target-typecheck-verify-swift + +// 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..35e57a1d26560 --- /dev/null +++ b/test/decl/protocol/special/coding/class_codable_computed_vars.swift @@ -0,0 +1,39 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// 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..2f575904eb0ad --- /dev/null +++ b/test/decl/protocol/special/coding/class_codable_inheritance.swift @@ -0,0 +1,55 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +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..20c51f3debb92 --- /dev/null +++ b/test/decl/protocol/special/coding/class_codable_simple.swift @@ -0,0 +1,34 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// 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..fb550a37f5959 --- /dev/null +++ b/test/decl/protocol/special/coding/class_codable_simple_extension.swift @@ -0,0 +1,36 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// 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..68a8c6cfb6d6e --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key.swift @@ -0,0 +1,99 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// 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..acc2b6702e031 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_empty.swift @@ -0,0 +1,10 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test + +// 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..06412c122c090 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_extension.swift @@ -0,0 +1,36 @@ +// RUN: %target-typecheck-verify-swift + +// 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..e55708285ea02 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_int_raw_type.swift @@ -0,0 +1,49 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test + +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..62dc7cbbdefcc --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_no_raw_type.swift @@ -0,0 +1,48 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test + +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..aefa0d7c0ea5c --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_partial_implementation.swift @@ -0,0 +1,147 @@ +// RUN: %target-typecheck-verify-swift + +// 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 "" } +} + +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 "" } + 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 "" } + 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 "" } + 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 "" } + 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 "" } + 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 "" } + 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..9e6a2a34b1ec8 --- /dev/null +++ b/test/decl/protocol/special/coding/enum_coding_key_string_raw_type.swift @@ -0,0 +1,52 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test + +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..054c7d77f9fb1 --- /dev/null +++ b/test/decl/protocol/special/coding/struct_codable_computed_vars.swift @@ -0,0 +1,36 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// 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..44acff8382ea5 --- /dev/null +++ b/test/decl/protocol/special/coding/struct_codable_simple.swift @@ -0,0 +1,31 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// 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..d1e505c415a20 --- /dev/null +++ b/test/decl/protocol/special/coding/struct_codable_simple_extension.swift @@ -0,0 +1,36 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +// 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