Skip to content

Commit 6754b86

Browse files
authored
Merge pull request #22379 from xedin/rdar-47787705
[ConstraintSystem] Detect invalid initializer references early
2 parents 07f1791 + 9a1e92e commit 6754b86

File tree

12 files changed

+361
-103
lines changed

12 files changed

+361
-103
lines changed

include/swift/AST/Expr.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -466,17 +466,21 @@ class alignas(8) Expr {
466466
/// This distinguishes static references to types, like Int, from metatype
467467
/// values, "someTy: Any.Type".
468468
bool isTypeReference(llvm::function_ref<Type(const Expr *)> getType =
469-
[](const Expr *E) -> Type {
470-
return E->getType();
471-
}) const;
469+
[](const Expr *E) -> Type { return E->getType(); },
470+
llvm::function_ref<Decl *(const Expr *)> getDecl =
471+
[](const Expr *E) -> Decl * {
472+
return nullptr;
473+
}) const;
472474

473475
/// Determine whether this expression refers to a statically-derived metatype.
474476
///
475477
/// This implies `isTypeReference`, but also requires that the referenced type
476478
/// is not an archetype or dependent type.
477479
bool isStaticallyDerivedMetatype(
478480
llvm::function_ref<Type(const Expr *)> getType =
479-
[](const Expr *E) -> Type { return E->getType(); }) const;
481+
[](const Expr *E) -> Type { return E->getType(); },
482+
llvm::function_ref<bool(const Expr *)> isTypeReference =
483+
[](const Expr *E) { return E->isTypeReference(); }) const;
480484

481485
/// isImplicit - Determines whether this expression was implicitly-generated,
482486
/// rather than explicitly written in the AST.

lib/AST/Expr.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ void Expr::forEachChildExpr(llvm::function_ref<Expr *(Expr *)> callback) {
440440
}
441441

442442
bool Expr::isTypeReference(
443-
llvm::function_ref<Type(const Expr *)> getType) const {
443+
llvm::function_ref<Type(const Expr *)> getType,
444+
llvm::function_ref<Decl *(const Expr *)> getDecl) const {
444445
// If the result isn't a metatype, there's nothing else to do.
445446
if (!getType(this)->is<AnyMetatypeType>())
446447
return false;
@@ -461,6 +462,12 @@ bool Expr::isTypeReference(
461462
if (auto memberRef = dyn_cast<MemberRefExpr>(expr))
462463
return isa<TypeDecl>(memberRef->getMember().getDecl());
463464

465+
// Any other expressions which might be referencing
466+
// a declaration e.g. not yet type-checked ones like
467+
// `UnresolvedDotExpr`.
468+
if (auto *decl = getDecl(expr))
469+
return isa<TypeDecl>(decl);
470+
464471
// When the base of a "." expression is ignored, look at the member.
465472
if (auto ignoredDot = dyn_cast<DotSyntaxBaseIgnoredExpr>(expr)) {
466473
expr = ignoredDot->getRHS();
@@ -470,13 +477,13 @@ bool Expr::isTypeReference(
470477
// Anything else is not statically derived.
471478
return false;
472479
} while (true);
473-
474480
}
475481

476482
bool Expr::isStaticallyDerivedMetatype(
477-
llvm::function_ref<Type(const Expr *)> getType) const {
483+
llvm::function_ref<Type(const Expr *)> getType,
484+
llvm::function_ref<bool(const Expr *)> isTypeReference) const {
478485
// The expression must first be a type reference.
479-
if (!isTypeReference(getType))
486+
if (!isTypeReference(this))
480487
return false;
481488

482489
auto type = getType(this)

lib/Sema/CSApply.cpp

Lines changed: 20 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -227,13 +227,31 @@ getImplicitMemberReferenceAccessSemantics(Expr *base, VarDecl *member,
227227
return member->getAccessSemanticsFromContext(DC, isAccessOnSelf);
228228
}
229229

230+
/// This extends functionality of `Expr::isTypeReference` with
231+
/// support for `UnresolvedDotExpr` and `UnresolvedMemberExpr`.
232+
/// This method could be used on not yet fully type-checked AST.
230233
bool ConstraintSystem::isTypeReference(const Expr *E) {
231-
return E->isTypeReference([&](const Expr *E) -> Type { return getType(E); });
234+
return E->isTypeReference(
235+
[&](const Expr *E) -> Type { return getType(E); },
236+
[&](const Expr *E) -> Decl * {
237+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E)) {
238+
return findResolvedMemberRef(
239+
getConstraintLocator(UDE, ConstraintLocator::Member));
240+
}
241+
242+
if (auto *UME = dyn_cast<UnresolvedMemberExpr>(E)) {
243+
return findResolvedMemberRef(
244+
getConstraintLocator(UME, ConstraintLocator::UnresolvedMember));
245+
}
246+
247+
return nullptr;
248+
});
232249
}
233250

234251
bool ConstraintSystem::isStaticallyDerivedMetatype(const Expr *E) {
235252
return E->isStaticallyDerivedMetatype(
236-
[&](const Expr *E) -> Type { return getType(E); });
253+
[&](const Expr *E) -> Type { return getType(E); },
254+
[&](const Expr *E) -> bool { return isTypeReference(E); });
237255
}
238256

239257
Type ConstraintSystem::getInstanceType(const TypeExpr *E) {
@@ -298,78 +316,6 @@ static bool buildObjCKeyPathString(KeyPathExpr *E,
298316
return true;
299317
}
300318

301-
/// Determine whether the given type refers to a non-final class (or
302-
/// dynamic self of one).
303-
static bool isNonFinalClass(Type type) {
304-
if (auto dynamicSelf = type->getAs<DynamicSelfType>())
305-
type = dynamicSelf->getSelfType();
306-
307-
if (auto classDecl = type->getClassOrBoundGenericClass())
308-
return !classDecl->isFinal();
309-
310-
if (auto archetype = type->getAs<ArchetypeType>())
311-
if (auto super = archetype->getSuperclass())
312-
return isNonFinalClass(super);
313-
314-
if (type->isExistentialType())
315-
return true;
316-
317-
return false;
318-
}
319-
320-
// Non-required constructors may not be not inherited. Therefore when
321-
// constructing a class object, either the metatype must be statically
322-
// derived (rather than an arbitrary value of metatype type) or the referenced
323-
// constructor must be required.
324-
static bool
325-
diagnoseInvalidDynamicConstructorReferences(ConstraintSystem &cs,
326-
Expr *base,
327-
DeclNameLoc memberRefLoc,
328-
ConstructorDecl *ctorDecl,
329-
bool SuppressDiagnostics) {
330-
auto &tc = cs.getTypeChecker();
331-
auto baseTy = cs.getType(base)->getRValueType();
332-
auto instanceTy = baseTy->getMetatypeInstanceType();
333-
334-
bool isStaticallyDerived =
335-
base->isStaticallyDerivedMetatype(
336-
[&](const Expr *expr) -> Type {
337-
return cs.getType(expr);
338-
});
339-
340-
// FIXME: The "hasClangNode" check here is a complete hack.
341-
if (isNonFinalClass(instanceTy) &&
342-
!isStaticallyDerived &&
343-
!ctorDecl->hasClangNode() &&
344-
!(ctorDecl->isRequired() ||
345-
ctorDecl->getDeclContext()->getSelfProtocolDecl())) {
346-
if (SuppressDiagnostics)
347-
return false;
348-
349-
tc.diagnose(memberRefLoc, diag::dynamic_construct_class, instanceTy)
350-
.highlight(base->getSourceRange());
351-
auto ctor = cast<ConstructorDecl>(ctorDecl);
352-
tc.diagnose(ctorDecl, diag::note_nonrequired_initializer,
353-
ctor->isImplicit(), ctor->getFullName());
354-
// Constructors cannot be called on a protocol metatype, because there is no
355-
// metatype to witness it.
356-
} else if (isa<ConstructorDecl>(ctorDecl) &&
357-
baseTy->is<MetatypeType>() &&
358-
instanceTy->isExistentialType()) {
359-
if (SuppressDiagnostics)
360-
return false;
361-
362-
if (isStaticallyDerived) {
363-
tc.diagnose(memberRefLoc, diag::construct_protocol_by_name, instanceTy)
364-
.highlight(base->getSourceRange());
365-
} else {
366-
tc.diagnose(memberRefLoc, diag::construct_protocol_value, baseTy)
367-
.highlight(base->getSourceRange());
368-
}
369-
}
370-
return true;
371-
}
372-
373319
/// Form a type checked expression for the index of a @dynamicMemberLookup
374320
/// subscript index parameter.
375321
/// The index expression will have a tuple type of `(dynamicMember: T)`.
@@ -877,14 +823,6 @@ namespace {
877823
if (auto baseMeta = baseTy->getAs<AnyMetatypeType>()) {
878824
baseIsInstance = false;
879825
baseTy = baseMeta->getInstanceType();
880-
881-
// If the member is a constructor, verify that it can be legally
882-
// referenced from this base.
883-
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
884-
if (!diagnoseInvalidDynamicConstructorReferences(cs, base, memberLoc,
885-
ctor, SuppressDiagnostics))
886-
return nullptr;
887-
}
888826
}
889827

890828
// Build a member reference.
@@ -2634,18 +2572,6 @@ namespace {
26342572
if (dre->getDecl()->getFullName() == cs.getASTContext().Id_self) {
26352573
// We have a reference to 'self'.
26362574
diagnoseBadInitRef = false;
2637-
2638-
// Special case -- in a protocol extension initializer with a class
2639-
// constrainted Self type, 'self' has archetype type, and only
2640-
// required initializers can be called.
2641-
if (cs.getType(dre)->getRValueType()->is<ArchetypeType>()) {
2642-
if (!diagnoseInvalidDynamicConstructorReferences(cs, base,
2643-
nameLoc,
2644-
ctor,
2645-
SuppressDiagnostics))
2646-
return nullptr;
2647-
}
2648-
26492575
// Make sure the reference to 'self' occurs within an initializer.
26502576
if (!dyn_cast_or_null<ConstructorDecl>(
26512577
cs.DC->getInnermostMethodContext())) {

lib/Sema/CSDiagnostics.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,3 +1506,27 @@ bool PartialApplicationFailure::diagnoseAsError() {
15061506
emitDiagnostic(anchor->getNameLoc(), diagnostic, kind);
15071507
return true;
15081508
}
1509+
1510+
bool InvalidDynamicInitOnMetatypeFailure::diagnoseAsError() {
1511+
auto *anchor = getRawAnchor();
1512+
emitDiagnostic(anchor->getLoc(), diag::dynamic_construct_class,
1513+
BaseType->getMetatypeInstanceType())
1514+
.highlight(BaseRange);
1515+
emitDiagnostic(Init, diag::note_nonrequired_initializer, Init->isImplicit(),
1516+
Init->getFullName());
1517+
return true;
1518+
}
1519+
1520+
bool InitOnProtocolMetatypeFailure::diagnoseAsError() {
1521+
auto *anchor = getRawAnchor();
1522+
if (IsStaticallyDerived) {
1523+
emitDiagnostic(anchor->getLoc(), diag::construct_protocol_by_name,
1524+
BaseType->getMetatypeInstanceType())
1525+
.highlight(BaseRange);
1526+
} else {
1527+
emitDiagnostic(anchor->getLoc(), diag::construct_protocol_value, BaseType)
1528+
.highlight(BaseRange);
1529+
}
1530+
1531+
return true;
1532+
}

lib/Sema/CSDiagnostics.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,70 @@ class PartialApplicationFailure final : public FailureDiagnostic {
707707
bool diagnoseAsError() override;
708708
};
709709

710+
class InvalidInitRefFailure : public FailureDiagnostic {
711+
protected:
712+
Type BaseType;
713+
const ConstructorDecl *Init;
714+
SourceRange BaseRange;
715+
716+
InvalidInitRefFailure(Expr *root, ConstraintSystem &cs, Type baseTy,
717+
const ConstructorDecl *init, SourceRange baseRange,
718+
ConstraintLocator *locator)
719+
: FailureDiagnostic(root, cs, locator), BaseType(baseTy), Init(init),
720+
BaseRange(baseRange) {}
721+
722+
public:
723+
bool diagnoseAsError() override = 0;
724+
};
725+
726+
/// Diagnose an attempt to construct an object of class type with a metatype
727+
/// value without using 'required' initializer:
728+
///
729+
/// ```swift
730+
/// class C {
731+
/// init(value: Int) {}
732+
/// }
733+
///
734+
/// func make<T: C>(type: T.Type) -> T {
735+
/// return T.init(value: 42)
736+
/// }
737+
/// ```
738+
class InvalidDynamicInitOnMetatypeFailure final : public InvalidInitRefFailure {
739+
public:
740+
InvalidDynamicInitOnMetatypeFailure(Expr *root, ConstraintSystem &cs,
741+
Type baseTy, const ConstructorDecl *init,
742+
SourceRange baseRange,
743+
ConstraintLocator *locator)
744+
: InvalidInitRefFailure(root, cs, baseTy, init, baseRange, locator) {}
745+
746+
bool diagnoseAsError() override;
747+
};
748+
749+
/// Diagnose an attempt to call initializer on protocol metatype:
750+
///
751+
/// ```swift
752+
/// protocol P {
753+
/// init(value: Int)
754+
/// }
755+
///
756+
/// func make(type: P.Type) -> P {
757+
/// return type.init(value: 42)
758+
/// }
759+
/// ```
760+
class InitOnProtocolMetatypeFailure final : public InvalidInitRefFailure {
761+
bool IsStaticallyDerived;
762+
763+
public:
764+
InitOnProtocolMetatypeFailure(Expr *root, ConstraintSystem &cs, Type baseTy,
765+
const ConstructorDecl *init,
766+
bool isStaticallyDerived, SourceRange baseRange,
767+
ConstraintLocator *locator)
768+
: InvalidInitRefFailure(root, cs, baseTy, init, baseRange, locator),
769+
IsStaticallyDerived(isStaticallyDerived) {}
770+
771+
bool diagnoseAsError() override;
772+
};
773+
710774
} // end namespace constraints
711775
} // end namespace swift
712776

lib/Sema/CSFix.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "CSDiagnostics.h"
2121
#include "ConstraintLocator.h"
2222
#include "ConstraintSystem.h"
23+
#include "OverloadChoice.h"
2324
#include "swift/AST/Expr.h"
2425
#include "swift/AST/Type.h"
2526
#include "swift/AST/Types.h"
@@ -274,3 +275,43 @@ AllowInvalidPartialApplication::create(bool isWarning, ConstraintSystem &cs,
274275
return new (cs.getAllocator())
275276
AllowInvalidPartialApplication(isWarning, cs, locator);
276277
}
278+
279+
bool AllowInvalidInitRef::diagnose(Expr *root, bool asNote) const {
280+
switch (Kind) {
281+
case RefKind::DynamicOnMetatype: {
282+
InvalidDynamicInitOnMetatypeFailure failure(
283+
root, getConstraintSystem(), BaseType, Init, BaseRange, getLocator());
284+
return failure.diagnose(asNote);
285+
}
286+
287+
case RefKind::ProtocolMetatype: {
288+
InitOnProtocolMetatypeFailure failure(root, getConstraintSystem(), BaseType,
289+
Init, IsStaticallyDerived, BaseRange,
290+
getLocator());
291+
return failure.diagnose(asNote);
292+
}
293+
}
294+
}
295+
296+
AllowInvalidInitRef *AllowInvalidInitRef::dynamicOnMetatype(
297+
ConstraintSystem &cs, Type baseTy, ConstructorDecl *init,
298+
SourceRange baseRange, ConstraintLocator *locator) {
299+
return create(RefKind::DynamicOnMetatype, cs, baseTy, init,
300+
/*isStaticallyDerived=*/false, baseRange, locator);
301+
}
302+
303+
AllowInvalidInitRef *AllowInvalidInitRef::onProtocolMetatype(
304+
ConstraintSystem &cs, Type baseTy, ConstructorDecl *init,
305+
bool isStaticallyDerived, SourceRange baseRange,
306+
ConstraintLocator *locator) {
307+
return create(RefKind::ProtocolMetatype, cs, baseTy, init,
308+
isStaticallyDerived, baseRange, locator);
309+
}
310+
311+
AllowInvalidInitRef *
312+
AllowInvalidInitRef::create(RefKind kind, ConstraintSystem &cs, Type baseTy,
313+
ConstructorDecl *init, bool isStaticallyDerived,
314+
SourceRange baseRange, ConstraintLocator *locator) {
315+
return new (cs.getAllocator()) AllowInvalidInitRef(
316+
cs, kind, baseTy, init, isStaticallyDerived, baseRange, locator);
317+
}

0 commit comments

Comments
 (0)