Skip to content

Commit 77a4527

Browse files
authored
Merge pull request #14097 from DougGregor/sr-5022-4.1
[4.1] [SR-5022] Diagnose an invalid use of non-polymorphic constructors
2 parents a8e71f7 + 7cdecef commit 77a4527

File tree

4 files changed

+106
-74
lines changed

4 files changed

+106
-74
lines changed

lib/Sema/CSApply.cpp

Lines changed: 89 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,85 @@ static bool buildObjCKeyPathString(KeyPathExpr *E,
321321
return true;
322322
}
323323

324+
/// Determine whether the given type refers to a non-final class (or
325+
/// dynamic self of one).
326+
static bool isNonFinalClass(Type type) {
327+
if (auto dynamicSelf = type->getAs<DynamicSelfType>())
328+
type = dynamicSelf->getSelfType();
329+
330+
if (auto classDecl = type->getClassOrBoundGenericClass())
331+
return !classDecl->isFinal();
332+
333+
if (auto archetype = type->getAs<ArchetypeType>())
334+
if (auto super = archetype->getSuperclass())
335+
return isNonFinalClass(super);
336+
337+
return false;
338+
}
339+
340+
// Non-required constructors may not be not inherited. Therefore when
341+
// constructing a class object, either the metatype must be statically
342+
// derived (rather than an arbitrary value of metatype type) or the referenced
343+
// constructor must be required.
344+
static bool
345+
diagnoseInvalidDynamicConstructorReferences(ConstraintSystem &cs,
346+
Expr *base,
347+
DeclNameLoc memberRefLoc,
348+
ConstructorDecl *ctorDecl,
349+
bool SuppressDiagnostics) {
350+
auto &tc = cs.getTypeChecker();
351+
auto baseTy = cs.getType(base)->getRValueType();
352+
auto instanceTy = baseTy->getRValueInstanceType();
353+
354+
bool isStaticallyDerived =
355+
base->isStaticallyDerivedMetatype(
356+
[&](const Expr *expr) -> Type {
357+
return cs.getType(expr);
358+
});
359+
360+
// 'super.' is always OK
361+
if (isa<SuperRefExpr>(base))
362+
return true;
363+
364+
// 'self.' reference with concrete type is OK
365+
if (isa<DeclRefExpr>(base) &&
366+
cast<DeclRefExpr>(base)->getDecl()->getBaseName() == tc.Context.Id_self &&
367+
!baseTy->is<ArchetypeType>())
368+
return true;
369+
370+
// FIXME: The "hasClangNode" check here is a complete hack.
371+
if (isNonFinalClass(instanceTy) &&
372+
!isStaticallyDerived &&
373+
!ctorDecl->hasClangNode() &&
374+
!(ctorDecl->isRequired() ||
375+
ctorDecl->getDeclContext()->getAsProtocolOrProtocolExtensionContext())) {
376+
if (SuppressDiagnostics)
377+
return false;
378+
379+
tc.diagnose(memberRefLoc, diag::dynamic_construct_class, instanceTy)
380+
.highlight(base->getSourceRange());
381+
auto ctor = cast<ConstructorDecl>(ctorDecl);
382+
tc.diagnose(ctorDecl, diag::note_nonrequired_initializer,
383+
ctor->isImplicit(), ctor->getFullName());
384+
// Constructors cannot be called on a protocol metatype, because there is no
385+
// metatype to witness it.
386+
} else if (isa<ConstructorDecl>(ctorDecl) &&
387+
baseTy->is<MetatypeType>() &&
388+
instanceTy->isExistentialType()) {
389+
if (SuppressDiagnostics)
390+
return false;
391+
392+
if (isStaticallyDerived) {
393+
tc.diagnose(memberRefLoc, diag::construct_protocol_by_name, instanceTy)
394+
.highlight(base->getSourceRange());
395+
} else {
396+
tc.diagnose(memberRefLoc, diag::construct_protocol_value, baseTy)
397+
.highlight(base->getSourceRange());
398+
}
399+
}
400+
return true;
401+
}
402+
324403
namespace {
325404

326405
/// \brief Rewrites an expression by applying the solution of a constraint
@@ -825,12 +904,12 @@ namespace {
825904
if (auto baseMeta = baseTy->getAs<AnyMetatypeType>()) {
826905
baseIsInstance = false;
827906
baseTy = baseMeta->getInstanceType();
907+
828908
// If the member is a constructor, verify that it can be legally
829909
// referenced from this base.
830910
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
831-
cs.setExprTypes(base);
832-
if (!tc.diagnoseInvalidDynamicConstructorReferences(base, memberLoc,
833-
baseMeta, ctor, SuppressDiagnostics))
911+
if (!diagnoseInvalidDynamicConstructorReferences(cs, base, memberLoc,
912+
ctor, SuppressDiagnostics))
834913
return nullptr;
835914
}
836915
}
@@ -2461,6 +2540,13 @@ namespace {
24612540
ConstructorDecl *ctor,
24622541
FunctionRefKind functionRefKind,
24632542
Type openedType) {
2543+
2544+
// If the member is a constructor, verify that it can be legally
2545+
// referenced from this base.
2546+
if (!diagnoseInvalidDynamicConstructorReferences(cs, base, nameLoc,
2547+
ctor, SuppressDiagnostics))
2548+
return nullptr;
2549+
24642550
// If the subexpression is a metatype, build a direct reference to the
24652551
// constructor.
24662552
if (cs.getType(base)->is<AnyMetatypeType>()) {
@@ -6805,67 +6891,6 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal,
68056891
return literal;
68066892
}
68076893

6808-
/// Determine whether the given type refers to a non-final class (or
6809-
/// dynamic self of one).
6810-
static bool isNonFinalClass(Type type) {
6811-
if (auto dynamicSelf = type->getAs<DynamicSelfType>())
6812-
type = dynamicSelf->getSelfType();
6813-
6814-
if (auto classDecl = type->getClassOrBoundGenericClass())
6815-
return !classDecl->isFinal();
6816-
6817-
if (auto archetype = type->getAs<ArchetypeType>())
6818-
if (auto super = archetype->getSuperclass())
6819-
return isNonFinalClass(super);
6820-
6821-
return false;
6822-
}
6823-
6824-
// Non-required constructors may not be not inherited. Therefore when
6825-
// constructing a class object, either the metatype must be statically
6826-
// derived (rather than an arbitrary value of metatype type) or the referenced
6827-
// constructor must be required.
6828-
bool
6829-
TypeChecker::diagnoseInvalidDynamicConstructorReferences(Expr *base,
6830-
DeclNameLoc memberRefLoc,
6831-
AnyMetatypeType *metaTy,
6832-
ConstructorDecl *ctorDecl,
6833-
bool SuppressDiagnostics) {
6834-
auto ty = metaTy->getInstanceType();
6835-
6836-
// FIXME: The "hasClangNode" check here is a complete hack.
6837-
if (isNonFinalClass(ty) &&
6838-
!base->isStaticallyDerivedMetatype() &&
6839-
!ctorDecl->hasClangNode() &&
6840-
!(ctorDecl->isRequired() ||
6841-
ctorDecl->getDeclContext()->getAsProtocolOrProtocolExtensionContext())) {
6842-
if (SuppressDiagnostics)
6843-
return false;
6844-
6845-
diagnose(memberRefLoc, diag::dynamic_construct_class, ty)
6846-
.highlight(base->getSourceRange());
6847-
auto ctor = cast<ConstructorDecl>(ctorDecl);
6848-
diagnose(ctorDecl, diag::note_nonrequired_initializer,
6849-
ctor->isImplicit(), ctor->getFullName());
6850-
// Constructors cannot be called on a protocol metatype, because there is no
6851-
// metatype to witness it.
6852-
} else if (isa<ConstructorDecl>(ctorDecl) &&
6853-
isa<MetatypeType>(metaTy) &&
6854-
ty->isExistentialType()) {
6855-
if (SuppressDiagnostics)
6856-
return false;
6857-
6858-
if (base->isStaticallyDerivedMetatype()) {
6859-
diagnose(memberRefLoc, diag::construct_protocol_by_name, ty)
6860-
.highlight(base->getSourceRange());
6861-
} else {
6862-
diagnose(memberRefLoc, diag::construct_protocol_value, metaTy)
6863-
.highlight(base->getSourceRange());
6864-
}
6865-
}
6866-
return true;
6867-
}
6868-
68696894
Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
68706895
ConstraintLocatorBuilder locator) {
68716896
TypeChecker &tc = cs.getTypeChecker();

lib/Sema/TypeChecker.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2483,15 +2483,6 @@ class TypeChecker final : public LazyResolver {
24832483
/// Diagnose assigning variable to itself.
24842484
bool diagnoseSelfAssignment(const Expr *E);
24852485

2486-
/// When referencing a class initializer, check that the base expression is
2487-
/// either a static metatype or that the initializer is 'required'.
2488-
bool
2489-
diagnoseInvalidDynamicConstructorReferences(Expr *base,
2490-
DeclNameLoc memberRefLoc,
2491-
AnyMetatypeType *metaTy,
2492-
ConstructorDecl *ctorDecl,
2493-
bool SuppressDiagnostics);
2494-
24952486
/// Builds a string representing a "default" generic argument list for
24962487
/// \p typeDecl. In general, this means taking the bound of each generic
24972488
/// parameter. The \p getPreferredType callback can be used to provide a

test/SILGen/protocol_extensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ extension ObjCInitRequirement {
813813
// rdar://problem/21370992 - delegation from an initializer in a
814814
// protocol extension to an @objc initializer in a class.
815815
class ObjCInitClass {
816-
@objc init() { }
816+
@objc required init() { }
817817
}
818818

819819
protocol ProtoDelegatesToObjC { }

test/decl/ext/protocol.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,22 @@ extension ExtendedProtocol where Self : DerivedWithAlias {
267267
func f4(x: NestedNominal) {}
268268
}
269269

270+
// rdar://problem/21991470 & https://bugs.swift.org/browse/SR-5022
271+
class NonPolymorphicInit {
272+
init() { } // expected-note {{selected non-required initializer 'init()'}}
273+
}
274+
275+
protocol EmptyProtocol { }
276+
277+
// The diagnostic is not very accurate, but at least we reject this.
278+
279+
extension EmptyProtocol where Self : NonPolymorphicInit {
280+
init(string: String) {
281+
self.init()
282+
// expected-error@-1 {{constructing an object of class type 'Self' with a metatype value must use a 'required' initializer}}
283+
}
284+
}
285+
270286
// ----------------------------------------------------------------------------
271287
// Using protocol extensions to satisfy requirements
272288
// ----------------------------------------------------------------------------

0 commit comments

Comments
 (0)