@@ -7944,6 +7944,67 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc,
7944
7944
tc.diagnose (classDecl, diag::class_without_init,
7945
7945
classDecl->getDeclaredType ());
7946
7946
7947
+ // HACK: We've got a special case to look out for and diagnose specifically to
7948
+ // improve the experience of seeing this, and mitigate some confusion.
7949
+ //
7950
+ // For a class A which inherits from Decodable class B, class A may have
7951
+ // additional members which prevent default initializer synthesis (and
7952
+ // inheritance of other initializers). The user may have assumed that this
7953
+ // case would synthesize Encodable/Decodable conformance for class A the same
7954
+ // way it may have for class B, or other classes.
7955
+ //
7956
+ // It is helpful to suggest here that the user may have forgotten to override
7957
+ // init(from:) (and encode(to:), if applicable) in a note, before we start
7958
+ // listing the members that prevented initializer synthesis.
7959
+ // TODO: Add a fixit along with this suggestion.
7960
+ if (auto *superclassDecl = classDecl->getSuperclassDecl ()) {
7961
+ ASTContext &C = tc.Context ;
7962
+ auto *decodableProto = C.getProtocol (KnownProtocolKind::Decodable);
7963
+ auto superclassType = superclassDecl->getDeclaredInterfaceType ();
7964
+ if (auto ref = tc.conformsToProtocol (superclassType, decodableProto,
7965
+ superclassDecl,
7966
+ ConformanceCheckOptions (),
7967
+ SourceLoc ())) {
7968
+ // super conforms to Decodable, so we've failed to inherit init(from:).
7969
+ // Let's suggest overriding it here.
7970
+ //
7971
+ // We're going to diagnose on the concrete init(from:) decl if it exists
7972
+ // and isn't implicit; otherwise, on the subclass itself.
7973
+ ValueDecl *diagDest = classDecl;
7974
+ auto initFrom = DeclName (C, C.Id_init , C.Id_from );
7975
+ auto result = tc.lookupMember (superclassDecl, superclassType, initFrom,
7976
+ NameLookupFlags::ProtocolMembers |
7977
+ NameLookupFlags::IgnoreAccessibility);
7978
+
7979
+ if (!result.empty () && !result.front ().getValueDecl ()->isImplicit ())
7980
+ diagDest = result.front ().getValueDecl ();
7981
+
7982
+ auto diagName = diag::decodable_suggest_overriding_init_here;
7983
+
7984
+ // This is also a bit of a hack, but the best place we've got at the
7985
+ // moment to suggest this.
7986
+ //
7987
+ // If the superclass also conforms to Encodable, it's quite
7988
+ // likely that the user forgot to override its encode(to:). In this case,
7989
+ // we can produce a slightly different diagnostic to suggest doing so.
7990
+ auto *encodableProto = C.getProtocol (KnownProtocolKind::Encodable);
7991
+ if ((ref = tc.conformsToProtocol (superclassType, encodableProto,
7992
+ superclassDecl,
7993
+ ConformanceCheckOptions (),
7994
+ SourceLoc ()))) {
7995
+ // We only want to produce this version of the diagnostic if the
7996
+ // subclass doesn't directly implement encode(to:).
7997
+ // The direct lookup here won't see an encode(to:) if it is inherited
7998
+ // from the superclass.
7999
+ auto encodeTo = DeclName (C, C.Id_encode , C.Id_to );
8000
+ if (classDecl->lookupDirect (encodeTo).empty ())
8001
+ diagName = diag::codable_suggest_overriding_init_here;
8002
+ }
8003
+
8004
+ tc.diagnose (diagDest, diagName);
8005
+ }
8006
+ }
8007
+
7947
8008
for (auto member : classDecl->getMembers ()) {
7948
8009
auto pbd = dyn_cast<PatternBindingDecl>(member);
7949
8010
if (!pbd)
@@ -8131,14 +8192,49 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
8131
8192
bool FoundMemberwiseInitializedProperty = false ;
8132
8193
bool SuppressDefaultInitializer = false ;
8133
8194
bool SuppressMemberwiseInitializer = false ;
8195
+ bool FoundSynthesizedInit = false ;
8134
8196
bool FoundDesignatedInit = false ;
8135
8197
decl->setAddedImplicitInitializers ();
8198
+
8199
+ // Before we look for constructors, we need to make sure that all synthesized
8200
+ // initializers are properly synthesized.
8201
+ //
8202
+ // NOTE: Lookups of synthesized initializers MUST come after
8203
+ // decl->setAddedImplicitInitializers() in case synthesis requires
8204
+ // protocol conformance checking, which might be recursive here.
8205
+ // FIXME: Disable this code and prevent _any_ implicit constructors from doing
8206
+ // this. Investigate why this hasn't worked otherwise.
8207
+ DeclName synthesizedInitializers[1 ] = {
8208
+ // init(from:) is synthesized by derived conformance to Decodable.
8209
+ DeclName (Context, DeclBaseName (Context.Id_init ), Context.Id_from )
8210
+ };
8211
+
8212
+ auto initializerIsSynthesized = [=](ConstructorDecl *initializer) {
8213
+ if (!initializer->isImplicit ())
8214
+ return false ;
8215
+
8216
+ for (auto &name : synthesizedInitializers)
8217
+ if (initializer->getFullName () == name)
8218
+ return true ;
8219
+
8220
+ return false ;
8221
+ };
8222
+
8223
+ for (auto &name : synthesizedInitializers) {
8224
+ synthesizeMemberForLookup (decl, name);
8225
+ }
8226
+
8136
8227
SmallPtrSet<CanType, 4 > initializerParamTypes;
8137
8228
llvm::SmallPtrSet<ConstructorDecl *, 4 > overriddenInits;
8138
8229
for (auto member : decl->getMembers ()) {
8139
8230
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
8140
- if (ctor->isDesignatedInit ())
8231
+ // Synthesized initializers others than the default initializer should
8232
+ // not prevent default initializer synthesis.
8233
+ if (initializerIsSynthesized (ctor)) {
8234
+ FoundSynthesizedInit = true ;
8235
+ } else if (ctor->isDesignatedInit ()) {
8141
8236
FoundDesignatedInit = true ;
8237
+ }
8142
8238
8143
8239
if (!ctor->isInvalid ())
8144
8240
initializerParamTypes.insert (getInitializerParamType (ctor));
@@ -8233,7 +8329,8 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
8233
8329
8234
8330
// We can't define these overrides if we have any uninitialized
8235
8331
// stored properties.
8236
- if (SuppressDefaultInitializer && !FoundDesignatedInit) {
8332
+ if (SuppressDefaultInitializer && !FoundDesignatedInit
8333
+ && !FoundSynthesizedInit) {
8237
8334
diagnoseClassWithoutInitializers (*this , classDecl);
8238
8335
return ;
8239
8336
}
@@ -8325,7 +8422,9 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
8325
8422
8326
8423
// ... unless there are uninitialized stored properties.
8327
8424
if (SuppressDefaultInitializer) {
8328
- diagnoseClassWithoutInitializers (*this , classDecl);
8425
+ if (!FoundSynthesizedInit)
8426
+ diagnoseClassWithoutInitializers (*this , classDecl);
8427
+
8329
8428
return ;
8330
8429
}
8331
8430
0 commit comments