Merge pull request #11045 from itaiferber/codable-class-fixes

Codable Class Fixes
This commit is contained in:
Itai Ferber
2017-07-24 15:42:45 -07:00
committed by GitHub
10 changed files with 559 additions and 130 deletions

View File

@@ -2072,6 +2072,20 @@ NOTE(codable_codingkeys_type_is_not_an_enum_here,none,
"cannot automatically synthesize %0 because 'CodingKeys' is not an enum", (Type))
NOTE(codable_codingkeys_type_does_not_conform_here,none,
"cannot automatically synthesize %0 because 'CodingKeys' does not conform to CodingKey", (Type))
NOTE(decodable_no_super_init_here,none,
"cannot automatically synthesize %0 because superclass does not have a callable %1", (DeclName, DeclName))
NOTE(decodable_super_init_not_designated_here,none,
"cannot automatically synthesize %0 because implementation would need to call %1, which is not designated", (DeclName, DeclName))
NOTE(decodable_inaccessible_super_init_here,none,
"cannot automatically synthesize %0 because implementation would need to call %1, which is inaccessible due to "
"'%select{private|fileprivate|internal|%error|%error}2' protection level",
(DeclName, DeclName, Accessibility))
NOTE(decodable_super_init_is_failable_here,none,
"cannot automatically synthesize %0 because implementation would need to call %1, which is failable", (DeclName, DeclName))
NOTE(decodable_suggest_overriding_init_here,none,
"did you mean to override 'init(from:)'?", ())
NOTE(codable_suggest_overriding_init_here,none,
"did you mean to override 'init(from:)' and 'encode(to:)'?", ())
// Dynamic Self
ERROR(dynamic_self_non_method,none,

View File

@@ -184,8 +184,7 @@ static bool
validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl,
NominalTypeDecl *target, ProtocolDecl *proto) {
// Look through all var decls in the given type.
// * Filter out lazy/computed vars (currently already done by
// getStoredProperties).
// * Filter out lazy/computed vars.
// * 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
@@ -197,6 +196,9 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl,
// against its CodingKey entry, it will get removed.
llvm::SmallDenseMap<Identifier, VarDecl *, 8> properties;
for (auto *varDecl : target->getStoredProperties(/*skipInaccessible=*/true)) {
if (varDecl->getAttrs().hasAttribute<LazyAttr>())
continue;
properties[varDecl->getName()] = varDecl;
}
@@ -242,7 +244,7 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl,
// we can skip them on encode. On decode, though, we can only skip them if
// they have a default value.
if (!properties.empty() &&
proto == tc.Context.getProtocol(KnownProtocolKind::Decodable)) {
proto->isSpecificProtocol(KnownProtocolKind::Decodable)) {
for (auto it = properties.begin(); it != properties.end(); ++it) {
if (it->second->getParentInitializer() != nullptr) {
// Var has a default value.
@@ -303,6 +305,9 @@ static CodingKeysValidity hasValidCodingKeysEnum(TypeChecker &tc,
return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false);
}
// If the decl hasn't been validated yet, do so.
tc.validateDecl(codingKeysTypeDecl);
// CodingKeys may be a typealias. If so, follow the alias to its canonical
// type.
auto codingKeysType = codingKeysTypeDecl->getDeclaredInterfaceType();
@@ -381,6 +386,9 @@ static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc,
// conforms to {En,De}codable, add it to the enum.
bool allConform = true;
for (auto *varDecl : target->getStoredProperties(/*skipInaccessible=*/true)) {
if (varDecl->getAttrs().hasAttribute<LazyAttr>())
continue;
auto conformance = varConformsToCodable(tc, target->getDeclContext(),
varDecl, proto);
switch (conformance) {
@@ -663,8 +671,9 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl) {
// Need to generate `try super.encode(to: container.superEncoder())`
// superEncoder()
auto *method = new (C) UnresolvedDeclRefExpr(
DeclName(C.Id_superEncoder), DeclRefKind::Ordinary, DeclNameLoc());
auto *method = new (C) UnresolvedDeclRefExpr(DeclName(C.Id_superEncoder),
DeclRefKind::Ordinary,
DeclNameLoc());
// container.superEncoder()
auto *superEncoderRef = new (C) DotSyntaxCallExpr(containerExpr,
@@ -944,51 +953,91 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl) {
/*Implicit=*/true);
auto *selfRef = createSelfDeclRef(initDecl);
auto *varExpr = new (C) UnresolvedDotExpr(
selfRef, SourceLoc(), DeclName(varDecl->getName()), DeclNameLoc(),
/*implicit=*/true);
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<ClassDecl>(targetDecl);
if (classDecl && superclassIsDecodable(classDecl)) {
// Need to generate `try super.init(from: container.superDecoder())`
// Classes which have a superclass must call super.init(from:) if the
// superclass is Decodable, or super.init() if it is not.
if (auto *classDecl = dyn_cast<ClassDecl>(targetDecl)) {
if (auto *superclassDecl = classDecl->getSuperclassDecl()) {
if (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) UnresolvedDotExpr(containerExpr, SourceLoc(),
DeclName(C.Id_superDecoder),
DeclNameLoc(), /*Implicit=*/true);
// container.superDecoder()
auto *superDecoderRef = new (C) DotSyntaxCallExpr(containerExpr,
SourceLoc(), method);
// container.superDecoder()
auto *superDecoderCall =
CallExpr::createImplicit(C, superDecoderRef, ArrayRef<Expr *>(),
ArrayRef<Identifier>());
// 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
auto *superRef = new (C) SuperRefExpr(initDecl->getImplicitSelfDecl(),
SourceLoc(), /*Implicit=*/true);
// super.init(from:)
auto initName = DeclName(C, C.Id_init, C.Id_from);
auto *initCall = new (C) UnresolvedDotExpr(superRef, SourceLoc(),
initName, DeclNameLoc(),
/*Implicit=*/true);
// super.init(from:)
auto *decodeCall = new (C) DotSyntaxCallExpr(superRef, SourceLoc(),
initDeclRef);
// super.decode(from: container.superDecoder())
Expr *args[1] = {superDecoderCall};
Identifier argLabels[1] = {C.Id_from};
auto *callExpr = CallExpr::createImplicit(C, initCall,
C.AllocateCopy(args),
C.AllocateCopy(argLabels));
// 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);
} else {
// The explicit constructor name is a compound name taking no arguments.
DeclName initName(C, C.Id_init, ArrayRef<Identifier>());
// try super.init(from: container.superDecoder())
auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(),
/*Implicit=*/true);
statements.push_back(tryExpr);
// We need to look this up in the superclass to see if it throws.
auto result = superclassDecl->lookupDirect(initName);
// We should have bailed one level up if this were not available.
assert(!result.empty());
// If the init is failable, we should have already bailed one level
// above.
ConstructorDecl *superInitDecl = cast<ConstructorDecl>(result.front());
assert(superInitDecl->getFailability() == OTK_None);
// super
auto *superRef = new (C) SuperRefExpr(initDecl->getImplicitSelfDecl(),
SourceLoc(), /*Implicit=*/true);
// super.init()
auto *superInitRef = new (C) UnresolvedDotExpr(superRef, SourceLoc(),
initName, DeclNameLoc(),
/*Implicit=*/true);
// super.init() call
Expr *callExpr = CallExpr::createImplicit(C, superInitRef,
ArrayRef<Expr *>(),
ArrayRef<Identifier>());
// If super.init throws, try super.init()
if (superInitDecl->hasThrows())
callExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(),
/*Implicit=*/true);
statements.push_back(callExpr);
}
}
}
auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
@@ -1053,12 +1102,10 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl,
// 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, target);
auto *initDecl = new (C) ConstructorDecl(name, SourceLoc(), OTK_None,
SourceLoc(), /*Throws=*/true,
SourceLoc(), selfDecl, paramList,
/*GenericParams=*/nullptr, target);
initDecl->setImplicit();
initDecl->setBodySynthesizer(deriveBodyDecodable_init);
@@ -1089,8 +1136,8 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl,
initDecl->setInterfaceType(interfaceType);
initDecl->setInitializerInterfaceType(initializerType);
initDecl->setAccessibility(
std::max(target->getFormalAccess(), Accessibility::Internal));
initDecl->setAccessibility(std::max(target->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
@@ -1111,10 +1158,80 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl,
///
/// \param target The type to validate.
///
/// \param requirement The requirement we want to synthesize.
///
/// \param proto The *codable protocol to check for validity.
static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target,
ProtocolDecl *proto) {
// First, look up if the type has a valid CodingKeys enum we can use.
ValueDecl *requirement, ProtocolDecl *proto) {
// Before we attempt to look up (or more importantly, synthesize) a CodingKeys
// entity on target, we need to make sure the type is otherwise valid.
//
// If we are synthesizing Decodable and the target is a class with a
// superclass, our synthesized init(from:) will need to call either
// super.init(from:) or super.init() depending on whether the superclass is
// Decodable itself.
//
// If the required initializer is not available, we shouldn't attempt to
// synthesize CodingKeys.
ASTContext &C = tc.Context;
auto *classDecl = dyn_cast<ClassDecl>(target);
if (proto->isSpecificProtocol(KnownProtocolKind::Decodable) && classDecl) {
if (auto *superclassDecl = classDecl->getSuperclassDecl()) {
DeclName memberName;
auto superType = superclassDecl->getDeclaredInterfaceType();
if (tc.conformsToProtocol(superType, proto, superclassDecl,
ConformanceCheckFlags::Used)) {
// super.init(from:) must be accessible.
memberName = cast<ConstructorDecl>(requirement)->getFullName();
} else {
// super.init() must be accessible.
// Passing an empty params array constructs a compound name with no
// arguments (as opposed to a simple name when omitted).
memberName = DeclName(C, DeclBaseName(C.Id_init),
ArrayRef<Identifier>());
}
auto result = tc.lookupMember(superclassDecl, superType, memberName);
if (result.empty()) {
// No super initializer for us to call.
tc.diagnose(superclassDecl, diag::decodable_no_super_init_here,
requirement->getFullName(), memberName);
return false;
} else if (result.size() > 1) {
// There are multiple results for this lookup. We'll end up producing a
// diagnostic later complaining about duplicate methods (if we haven't
// already), so just bail with a general error.
return false;
} else {
auto *initializer =
cast<ConstructorDecl>(result.front().getValueDecl());
if (!initializer->isDesignatedInit()) {
// We must call a superclass's designated initializer.
tc.diagnose(initializer,
diag::decodable_super_init_not_designated_here,
requirement->getFullName(), memberName);
return false;
} else if (!initializer->isAccessibleFrom(target)) {
// Cannot call an inaccessible method.
auto accessScope = initializer->getFormalAccessScope(target);
tc.diagnose(initializer, diag::decodable_inaccessible_super_init_here,
requirement->getFullName(), memberName,
accessScope.accessibilityForDiagnostics());
return false;
} else if (initializer->getFailability() != OTK_None) {
// We can't call super.init() if it's failable, since init(from:)
// isn't failable.
tc.diagnose(initializer, diag::decodable_super_init_is_failable_here,
requirement->getFullName(), memberName);
return false;
}
}
}
}
// If the target already has a valid CodingKeys enum, we won't need to
// synthesize one.
auto validity = hasValidCodingKeysEnum(tc, target, proto);
// We found a type, but it wasn't valid.
@@ -1172,23 +1289,17 @@ ValueDecl *DerivedConformance::deriveEncodable(TypeChecker &tc,
auto diagnosticTransaction = DiagnosticTransaction(tc.Context.Diags);
tc.diagnose(target, diag::type_does_not_conform, target->getDeclaredType(),
encodableType);
tc.diagnose(requirement, diag::no_witnesses, diag::RequirementKind::Func,
requirement->getFullName(), encodableType, /*AddFixIt=*/false);
// Check other preconditions for synthesized conformance.
// This synthesizes a CodingKeys enum if possible.
ValueDecl *witness = nullptr;
if (canSynthesize(tc, target, encodableProto))
witness = deriveEncodable_encode(tc, parentDecl, target);
if (witness == nullptr) {
// We didn't end up synthesizing encode(to:).
tc.diagnose(requirement, diag::no_witnesses, diag::RequirementKind::Func,
requirement->getFullName(), encodableType, /*AddFixIt=*/false);
} else {
// We succeeded -- no need to output the false error generated above.
if (canSynthesize(tc, target, requirement, encodableProto)) {
diagnosticTransaction.abort();
return deriveEncodable_encode(tc, parentDecl, target);
}
return witness;
return nullptr;
}
ValueDecl *DerivedConformance::deriveDecodable(TypeChecker &tc,
@@ -1222,22 +1333,16 @@ ValueDecl *DerivedConformance::deriveDecodable(TypeChecker &tc,
auto diagnosticTransaction = DiagnosticTransaction(tc.Context.Diags);
tc.diagnose(target, diag::type_does_not_conform, target->getDeclaredType(),
decodableType);
tc.diagnose(requirement, diag::no_witnesses,
diag::RequirementKind::Constructor, requirement->getFullName(),
decodableType, /*AddFixIt=*/false);
// Check other preconditions for synthesized conformance.
// This synthesizes a CodingKeys enum if possible.
ValueDecl *witness = nullptr;
if (canSynthesize(tc, target, decodableProto))
witness = deriveDecodable_init(tc, parentDecl, target);
if (witness == nullptr) {
// We didn't end up synthesizing init(from:).
tc.diagnose(requirement, diag::no_witnesses,
diag::RequirementKind::Constructor, requirement->getFullName(),
decodableType, /*AddFixIt=*/false);
} else {
// We succeeded -- no need to output the false error generated above.
if (canSynthesize(tc, target, requirement, decodableProto)) {
diagnosticTransaction.abort();
return deriveDecodable_init(tc, parentDecl, target);
}
return witness;
return nullptr;
}

View File

@@ -407,8 +407,9 @@ static bool canSynthesizeCodingKey(TypeChecker &tc, Decl *parentDecl,
return false;
}
if (!enumDecl->getInherited().empty() &&
enumDecl->getInherited().front().isError())
auto inherited = enumDecl->getInherited();
if (!inherited.empty() && inherited.front().wasValidated() &&
inherited.front().isError())
return false;
// If it meets all of those requirements, we can synthesize CodingKey

View File

@@ -343,8 +343,9 @@ static bool canSynthesizeRawRepresentable(TypeChecker &tc, Decl *parentDecl,
auto parentDC = cast<DeclContext>(parentDecl);
rawType = parentDC->mapTypeIntoContext(rawType);
if (!enumDecl->getInherited().empty() &&
enumDecl->getInherited().front().isError())
auto inherited = enumDecl->getInherited();
if (!inherited.empty() && inherited.front().wasValidated() &&
inherited.front().isError())
return false;
// The raw type must be Equatable, so that we have a suitable ~= for

View File

@@ -7944,6 +7944,67 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc,
tc.diagnose(classDecl, diag::class_without_init,
classDecl->getDeclaredType());
// HACK: We've got a special case to look out for and diagnose specifically to
// improve the experience of seeing this, and mitigate some confusion.
//
// For a class A which inherits from Decodable class B, class A may have
// additional members which prevent default initializer synthesis (and
// inheritance of other initializers). The user may have assumed that this
// case would synthesize Encodable/Decodable conformance for class A the same
// way it may have for class B, or other classes.
//
// It is helpful to suggest here that the user may have forgotten to override
// init(from:) (and encode(to:), if applicable) in a note, before we start
// listing the members that prevented initializer synthesis.
// TODO: Add a fixit along with this suggestion.
if (auto *superclassDecl = classDecl->getSuperclassDecl()) {
ASTContext &C = tc.Context;
auto *decodableProto = C.getProtocol(KnownProtocolKind::Decodable);
auto superclassType = superclassDecl->getDeclaredInterfaceType();
if (auto ref = tc.conformsToProtocol(superclassType, decodableProto,
superclassDecl,
ConformanceCheckOptions(),
SourceLoc())) {
// super conforms to Decodable, so we've failed to inherit init(from:).
// Let's suggest overriding it here.
//
// We're going to diagnose on the concrete init(from:) decl if it exists
// and isn't implicit; otherwise, on the subclass itself.
ValueDecl *diagDest = classDecl;
auto initFrom = DeclName(C, C.Id_init, C.Id_from);
auto result = tc.lookupMember(superclassDecl, superclassType, initFrom,
NameLookupFlags::ProtocolMembers |
NameLookupFlags::IgnoreAccessibility);
if (!result.empty() && !result.front().getValueDecl()->isImplicit())
diagDest = result.front().getValueDecl();
auto diagName = diag::decodable_suggest_overriding_init_here;
// This is also a bit of a hack, but the best place we've got at the
// moment to suggest this.
//
// If the superclass also conforms to Encodable, it's quite
// likely that the user forgot to override its encode(to:). In this case,
// we can produce a slightly different diagnostic to suggest doing so.
auto *encodableProto = C.getProtocol(KnownProtocolKind::Encodable);
if ((ref = tc.conformsToProtocol(superclassType, encodableProto,
superclassDecl,
ConformanceCheckOptions(),
SourceLoc()))) {
// We only want to produce this version of the diagnostic if the
// subclass doesn't directly implement encode(to:).
// The direct lookup here won't see an encode(to:) if it is inherited
// from the superclass.
auto encodeTo = DeclName(C, C.Id_encode, C.Id_to);
if (classDecl->lookupDirect(encodeTo).empty())
diagName = diag::codable_suggest_overriding_init_here;
}
tc.diagnose(diagDest, diagName);
}
}
for (auto member : classDecl->getMembers()) {
auto pbd = dyn_cast<PatternBindingDecl>(member);
if (!pbd)
@@ -8131,14 +8192,49 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
bool FoundMemberwiseInitializedProperty = false;
bool SuppressDefaultInitializer = false;
bool SuppressMemberwiseInitializer = false;
bool FoundSynthesizedInit = false;
bool FoundDesignatedInit = false;
decl->setAddedImplicitInitializers();
// Before we look for constructors, we need to make sure that all synthesized
// initializers are properly synthesized.
//
// NOTE: Lookups of synthesized initializers MUST come after
// decl->setAddedImplicitInitializers() in case synthesis requires
// protocol conformance checking, which might be recursive here.
// FIXME: Disable this code and prevent _any_ implicit constructors from doing
// this. Investigate why this hasn't worked otherwise.
DeclName synthesizedInitializers[1] = {
// init(from:) is synthesized by derived conformance to Decodable.
DeclName(Context, DeclBaseName(Context.Id_init), Context.Id_from)
};
auto initializerIsSynthesized = [=](ConstructorDecl *initializer) {
if (!initializer->isImplicit())
return false;
for (auto &name : synthesizedInitializers)
if (initializer->getFullName() == name)
return true;
return false;
};
for (auto &name : synthesizedInitializers) {
synthesizeMemberForLookup(decl, name);
}
SmallPtrSet<CanType, 4> initializerParamTypes;
llvm::SmallPtrSet<ConstructorDecl *, 4> overriddenInits;
for (auto member : decl->getMembers()) {
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
if (ctor->isDesignatedInit())
// Synthesized initializers others than the default initializer should
// not prevent default initializer synthesis.
if (initializerIsSynthesized(ctor)) {
FoundSynthesizedInit = true;
} else if (ctor->isDesignatedInit()) {
FoundDesignatedInit = true;
}
if (!ctor->isInvalid())
initializerParamTypes.insert(getInitializerParamType(ctor));
@@ -8233,7 +8329,8 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
// We can't define these overrides if we have any uninitialized
// stored properties.
if (SuppressDefaultInitializer && !FoundDesignatedInit) {
if (SuppressDefaultInitializer && !FoundDesignatedInit
&& !FoundSynthesizedInit) {
diagnoseClassWithoutInitializers(*this, classDecl);
return;
}
@@ -8325,7 +8422,9 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
// ... unless there are uninitialized stored properties.
if (SuppressDefaultInitializer) {
diagnoseClassWithoutInitializers(*this, classDecl);
if (!FoundSynthesizedInit)
diagnoseClassWithoutInitializers(*this, classDecl);
return;
}

View File

@@ -0,0 +1,49 @@
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown
// A class with no initializers (which has non-initialized properties so a
// default constructor can be synthesized) should produce an error.
class NoInitializers { // expected-error {{class 'NoInitializers' has no initializers}}
// expected-note@-1 {{did you mean 'deinit'?}}
var x: Double // expected-note {{stored property 'x' without initial value prevents synthesized initializers}}
func foo() {
// The class should not receive a default constructor.
let _ = NoInitializers.init() // expected-error {{type 'NoInitializers' has no member 'init'}}
}
}
// A similar class with Codable properties adopting Codable should get a
// synthesized init(from:), and thus not warn.
class CodableNoExplicitInitializers : Codable {
var x: Double
func foo() {
// The class should receive a synthesized init(from:) and encode(to:)
let _ = CodableNoExplicitInitializers.init(from:)
let _ = CodableNoExplicitInitializers.encode(to:)
// It should not, however, receive a default constructor.
let _ = CodableNoExplicitInitializers.init() // expected-error {{missing argument for parameter 'from' in call}}
}
}
// A class with all initialized properties should receive a default constructor.
class DefaultConstructed {
var x: Double = .pi
func foo() {
let _ = DefaultConstructed.init()
}
}
// A class with all initialized, Codable properties adopting Codable should get
// the default constructor, along with a synthesized init(from:).
class CodableDefaultConstructed : Codable {
var x: Double = .pi
func foo() {
let _ = CodableDefaultConstructed.init()
let _ = CodableDefaultConstructed.init(from:)
let _ = CodableDefaultConstructed.encode(to:)
}
}

View File

@@ -8,12 +8,12 @@ class C1 : Codable {
var c: Nested = Nested()
// CHECK: error: type 'C1' does not conform to protocol 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'C1.Nested' does not conform to 'Decodable'
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'C1.Nested' does not conform to 'Decodable'
// CHECK: error: type 'C1' does not conform to protocol 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'C1.Nested' does not conform to 'Encodable'
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'C1.Nested' does not conform to 'Encodable'
}
// Codable class with non-enum CodingKeys.
@@ -30,12 +30,12 @@ class C2 : Codable {
}
// CHECK: error: type 'C2' does not conform to protocol 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' is not an enum
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' is not an enum
// CHECK: error: type 'C2' does not conform to protocol 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' is not an enum
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' is not an enum
}
// Codable class with CodingKeys not conforming to CodingKey.
@@ -51,12 +51,12 @@ class C3 : Codable {
}
// CHECK: error: type 'C3' does not conform to protocol 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' does not conform to CodingKey
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' does not conform to CodingKey
// CHECK: error: type 'C3' does not conform to protocol 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' does not conform to CodingKey
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' does not conform to CodingKey
}
// Codable class with extraneous CodingKeys
@@ -75,16 +75,16 @@ class C4 : Codable {
}
// CHECK: error: type 'C4' does not conform to protocol 'Decodable'
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: CodingKey case 'a2' does not match any stored properties
// CHECK: note: CodingKey case 'b2' does not match any stored properties
// CHECK: note: CodingKey case 'c2' does not match any stored properties
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: error: type 'C4' does not conform to protocol 'Encodable'
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
// CHECK: note: CodingKey case 'a2' does not match any stored properties
// CHECK: note: CodingKey case 'b2' does not match any stored properties
// CHECK: note: CodingKey case 'c2' does not match any stored properties
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
}
// Codable class with non-decoded property (which has no default value).
@@ -98,12 +98,12 @@ class C5 : Codable {
case c
}
// CHECK: error: type 'C5' does not conform to protocol 'Decodable'
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
// CHECK: error: class 'C5' has no initializers
// CHECK: note: stored property 'b' without initial value prevents synthesized initializers
// CHECK: error: type 'C5' does not conform to protocol 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
}
// Codable class with non-decoded property (which has no default value).
@@ -122,8 +122,8 @@ class C6 : Codable {
}
// CHECK: error: type 'C6' does not conform to protocol 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
}
// Classes cannot yet synthesize Encodable or Decodable in extensions.

View File

@@ -0,0 +1,95 @@
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown
// Non-Decodable superclasses of synthesized Decodable classes must implement
// init().
class NonDecodableSuper { // expected-note {{cannot automatically synthesize 'init(from:)' because superclass does not have a callable 'init()'}}
init(_: Int) {}
}
class NonDecodableSub : NonDecodableSuper, Decodable { // expected-error {{type 'NonDecodableSub' does not conform to protocol 'Decodable'}}
}
// Non-Decodable superclasses of synthesized Decodable classes must have
// designated init()'s.
class NonDesignatedNonDecodableSuper {
convenience init() { // expected-note {{cannot automatically synthesize 'init(from:)' because implementation would need to call 'init()', which is not designated}}
self.init(42)
}
init(_: Int) {}
}
class NonDesignatedNonDecodableSub : NonDesignatedNonDecodableSuper, Decodable { // expected-error {{type 'NonDesignatedNonDecodableSub' does not conform to protocol 'Decodable'}}
}
// Non-Decodable superclasses of synthesized Decodable classes must have an
// accessible init().
class InaccessibleNonDecodableSuper {
private init() {} // expected-note {{cannot automatically synthesize 'init(from:)' because implementation would need to call 'init()', which is inaccessible due to 'private' protection level}}
}
class InaccessibleNonDecodableSub : InaccessibleNonDecodableSuper, Decodable { // expected-error {{type 'InaccessibleNonDecodableSub' does not conform to protocol 'Decodable'}}
}
// Non-Decodable superclasses of synthesized Decodable classes must have a
// non-failable init().
class FailableNonDecodableSuper {
init?() {} // expected-note {{cannot automatically synthesize 'init(from:)' because implementation would need to call 'init()', which is failable}}
}
class FailableNonDecodableSub : FailableNonDecodableSuper, Decodable { // expected-error {{type 'FailableNonDecodableSub' does not conform to protocol 'Decodable'}}
}
// Subclasses of classes whose Decodable synthesis fails should not inherit
// conformance.
class FailedSynthesisDecodableSuper : Decodable { // expected-error {{type 'FailedSynthesisDecodableSuper' does not conform to protocol 'Decodable'}}
enum CodingKeys : String, CodingKey {
case nonexistent // expected-note {{CodingKey case 'nonexistent' does not match any stored properties}}
}
}
class FailedSynthesisDecodableSub : FailedSynthesisDecodableSuper { // expected-note {{did you mean 'init'?}}
func foo() {
// Decodable should fail to synthesis or be inherited.
let _ = FailedSynthesisDecodableSub.init(from:) // expected-error {{type 'FailedSynthesisDecodableSub' has no member 'init(from:)'}}
}
}
// Subclasses of Decodable classes which can't inherit their initializers should
// produce diagnostics.
class DecodableSuper : Decodable {
var value = 5
}
class DecodableSubWithoutInitialValue : DecodableSuper { // expected-error {{class 'DecodableSubWithoutInitialValue' has no initializers}}
// expected-note@-1 {{did you mean to override 'init(from:)'?}}
var value2: Int // expected-note {{stored property 'value2' without initial value prevents synthesized initializers}}
}
class DecodableSubWithInitialValue : DecodableSuper {
var value2 = 10
}
// Subclasses of Codable classes which can't inherit their initializers should
// produce diagnostics.
class CodableSuper : Codable {
var value = 5
}
class CodableSubWithoutInitialValue : CodableSuper { // expected-error {{class 'CodableSubWithoutInitialValue' has no initializers}}
// expected-note@-1 {{did you mean to override 'init(from:)' and 'encode(to:)'?}}
var value2: Int // expected-note {{stored property 'value2' without initial value prevents synthesized initializers}}
}
// We should only mention encode(to:) in the diagnostic if the subclass does not
// override it.
class EncodableSubWithoutInitialValue : CodableSuper { // expected-error {{class 'EncodableSubWithoutInitialValue' has no initializers}}
// expected-note@-1 {{did you mean to override 'init(from:)'?}}
var value2: Int // expected-note {{stored property 'value2' without initial value prevents synthesized initializers}}
override func encode(to: Encoder) throws {}
}
class CodableSubWithInitialValue : CodableSuper {
var value2 = 10
}

View File

@@ -6,14 +6,6 @@ struct S1 : Codable {
var a: String = ""
var b: Int = 0
var c: Nested = Nested()
// CHECK: error: type 'S1' does not conform to protocol 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'S1.Nested' does not conform to 'Decodable'
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: error: type 'S1' does not conform to protocol 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'S1.Nested' does not conform to 'Encodable'
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
}
// Codable struct with non-enum CodingKeys.
@@ -28,14 +20,6 @@ struct S2 : Codable {
init?(stringValue: String) {}
init?(intValue: Int) {}
}
// CHECK: error: type 'S2' does not conform to protocol 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' is not an enum
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: error: type 'S2' does not conform to protocol 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' is not an enum
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
}
// Codable struct with CodingKeys not conforming to CodingKey.
@@ -49,14 +33,6 @@ struct S3 : Codable {
case b
case c
}
// CHECK: error: type 'S3' does not conform to protocol 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' does not conform to CodingKey
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: error: type 'S3' does not conform to protocol 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' does not conform to CodingKey
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
}
// Codable struct with extraneous CodingKeys
@@ -73,18 +49,6 @@ struct S4 : Codable {
case c
case c2
}
// CHECK: error: type 'S4' does not conform to protocol 'Decodable'
// CHECK: note: CodingKey case 'a2' does not match any stored properties
// CHECK: note: CodingKey case 'b2' does not match any stored properties
// CHECK: note: CodingKey case 'c2' does not match any stored properties
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: error: type 'S4' does not conform to protocol 'Encodable'
// CHECK: note: CodingKey case 'a2' does not match any stored properties
// CHECK: note: CodingKey case 'b2' does not match any stored properties
// CHECK: note: CodingKey case 'c2' does not match any stored properties
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
}
// Codable struct with non-decoded property (which has no default value).
@@ -97,14 +61,60 @@ struct S5 : Codable {
case a
case c
}
// CHECK: error: type 'S5' does not conform to protocol 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
}
// Structs cannot yet synthesize Encodable or Decodable in extensions.
struct S6 {}
extension S6 : Codable {}
// Decodable diagnostics are output first here {
// CHECK: error: type 'S1' does not conform to protocol 'Decodable'
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'S1.Nested' does not conform to 'Decodable'
// CHECK: error: type 'S2' does not conform to protocol 'Decodable'
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' is not an enum
// CHECK: error: type 'S3' does not conform to protocol 'Decodable'
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' does not conform to CodingKey
// CHECK: error: type 'S4' does not conform to protocol 'Decodable'
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: CodingKey case 'a2' does not match any stored properties
// CHECK: note: CodingKey case 'b2' does not match any stored properties
// CHECK: note: CodingKey case 'c2' does not match any stored properties
// CHECK: error: type 'S5' does not conform to protocol 'Decodable'
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
// CHECK: error: implementation of 'Decodable' cannot be automatically synthesized in an extension yet
// }
// Encodable {
// CHECK: error: type 'S1' does not conform to protocol 'Encodable'
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'S1.Nested' does not conform to 'Encodable'
// CHECK: error: type 'S2' does not conform to protocol 'Encodable'
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' is not an enum
// CHECK: error: type 'S3' does not conform to protocol 'Encodable'
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' does not conform to CodingKey
// CHECK: error: type 'S4' does not conform to protocol 'Encodable'
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
// CHECK: note: CodingKey case 'a2' does not match any stored properties
// CHECK: note: CodingKey case 'b2' does not match any stored properties
// CHECK: note: CodingKey case 'c2' does not match any stored properties
// CHECK: error: implementation of 'Encodable' cannot be automatically synthesized in an extension yet
// }

View File

@@ -0,0 +1,55 @@
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown
// Structs with no initializers should get a memberwise initializer.
struct NoInitializers {
var x: Double // need not have an initial value
func foo() {
// The struct should receive a memberwise initializer.
let _ = NoInitializers.init(x:)
}
}
// A similar struct with Codable properties adopting Codable should get a
// synthesized init(from:), along with the memberwise initializer.
struct CodableNoExplicitInitializers : Codable {
var x: Double
func foo() {
// The struct should receive a synthesized init(from:) and encode(to:).
let _ = CodableNoExplicitInitializers.init(from:)
let _ = CodableNoExplicitInitializers.encode(to:)
// It should still receive a memberwise initializer.
let _ = CodableNoExplicitInitializers.init(x:)
}
}
// The same should hold for structs whose members all have initial values.
struct InitialValueNoInitializers {
var x: Double = .pi
func foo() {
// The struct should receive a memberwise initializer.
let _ = InitialValueNoInitializers.init(x:)
// The struct should receive a no-argument initializer.
let _ = InitialValueNoInitializers.init()
}
}
struct InitialValueCodableNoExplicitInitializers : Codable {
var x: Double = .pi
func foo() {
// The struct should receive a synthesized init(from:) and encode(to:).
let _ = InitialValueCodableNoExplicitInitializers.init(from:)
let _ = InitialValueCodableNoExplicitInitializers.encode(to:)
// It should still receive a memberwise initializer.
let _ = InitialValueCodableNoExplicitInitializers.init(x:)
// It should still receive a no-argument initializer.
let _ = InitialValueCodableNoExplicitInitializers.init()
}
}