mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #11045 from itaiferber/codable-class-fixes
Codable Class Fixes
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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:)
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
// }
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user