Skip to content

Commit 2ea00c3

Browse files
authored
Merge pull request #22442 from xedin/diagnose-construction-using-non-cost-metatype
[ConstraintSystem] Detect invalid implicit ref to initializer on non-…
2 parents 0d6cdd4 + 1d42e16 commit 2ea00c3

File tree

8 files changed

+77
-21
lines changed

8 files changed

+77
-21
lines changed

lib/AST/Expr.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,11 @@ bool Expr::isTypeReference(
474474
continue;
475475
}
476476

477+
if (auto *USE = dyn_cast<UnresolvedSpecializeExpr>(expr)) {
478+
expr = USE->getSubExpr();
479+
continue;
480+
}
481+
477482
// Anything else is not statically derived.
478483
return false;
479484
} while (true);

lib/Sema/CSApply.cpp

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ getImplicitMemberReferenceAccessSemantics(Expr *base, VarDecl *member,
232232
/// This method could be used on not yet fully type-checked AST.
233233
bool ConstraintSystem::isTypeReference(const Expr *E) {
234234
return E->isTypeReference(
235-
[&](const Expr *E) -> Type { return getType(E); },
235+
[&](const Expr *E) -> Type { return simplifyType(getType(E)); },
236236
[&](const Expr *E) -> Decl * {
237237
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E)) {
238238
return findResolvedMemberRef(
@@ -244,13 +244,17 @@ bool ConstraintSystem::isTypeReference(const Expr *E) {
244244
getConstraintLocator(UME, ConstraintLocator::UnresolvedMember));
245245
}
246246

247+
if (isa<OverloadSetRefExpr>(E))
248+
return findResolvedMemberRef(
249+
getConstraintLocator(const_cast<Expr *>(E)));
250+
247251
return nullptr;
248252
});
249253
}
250254

251255
bool ConstraintSystem::isStaticallyDerivedMetatype(const Expr *E) {
252256
return E->isStaticallyDerivedMetatype(
253-
[&](const Expr *E) -> Type { return getType(E); },
257+
[&](const Expr *E) -> Type { return simplifyType(getType(E)); },
254258
[&](const Expr *E) -> bool { return isTypeReference(E); });
255259
}
256260

@@ -7395,24 +7399,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
73957399
if (auto metaTy = cs.getType(fn)->getAs<AnyMetatypeType>()) {
73967400
auto ty = metaTy->getInstanceType();
73977401

7398-
if (!cs.isTypeReference(fn)) {
7399-
bool isExistentialType = false;
7400-
// If this is an attempt to initialize existential type.
7401-
if (auto metaType = cs.getType(fn)->getAs<MetatypeType>()) {
7402-
auto instanceType = metaType->getInstanceType();
7403-
isExistentialType = instanceType->isExistentialType();
7404-
}
7405-
7406-
if (!isExistentialType) {
7407-
// If the metatype value isn't a type expression,
7408-
// the user should reference '.init' explicitly, for clarity.
7409-
cs.TC
7410-
.diagnose(apply->getArg()->getStartLoc(),
7411-
diag::missing_init_on_metatype_initialization)
7412-
.fixItInsert(apply->getArg()->getStartLoc(), ".init");
7413-
}
7414-
}
7415-
74167402
// If we're "constructing" a tuple type, it's simply a conversion.
74177403
if (auto tupleTy = ty->getAs<TupleType>()) {
74187404
// FIXME: Need an AST to represent this properly.

lib/Sema/CSDiagnostics.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,3 +1530,11 @@ bool InitOnProtocolMetatypeFailure::diagnoseAsError() {
15301530

15311531
return true;
15321532
}
1533+
1534+
bool ImplicitInitOnNonConstMetatypeFailure::diagnoseAsError() {
1535+
auto *apply = cast<ApplyExpr>(getRawAnchor());
1536+
auto loc = apply->getArg()->getStartLoc();
1537+
emitDiagnostic(loc, diag::missing_init_on_metatype_initialization)
1538+
.fixItInsert(loc, ".init");
1539+
return true;
1540+
}

lib/Sema/CSDiagnostics.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,25 @@ class InitOnProtocolMetatypeFailure final : public InvalidInitRefFailure {
771771
bool diagnoseAsError() override;
772772
};
773773

774+
/// Diagnose an attempt to construct an instance using non-constant
775+
/// metatype base without explictly specifying `init`:
776+
///
777+
/// ```swift
778+
/// let foo = Int.self
779+
/// foo(0) // should be `foo.init(0)`
780+
/// ```
781+
class ImplicitInitOnNonConstMetatypeFailure final
782+
: public InvalidInitRefFailure {
783+
public:
784+
ImplicitInitOnNonConstMetatypeFailure(Expr *root, ConstraintSystem &cs,
785+
Type baseTy,
786+
const ConstructorDecl *init,
787+
ConstraintLocator *locator)
788+
: InvalidInitRefFailure(root, cs, baseTy, init, SourceRange(), locator) {}
789+
790+
bool diagnoseAsError() override;
791+
};
792+
774793
} // end namespace constraints
775794
} // end namespace swift
776795

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,12 @@ bool AllowInvalidInitRef::diagnose(Expr *root, bool asNote) const {
290290
getLocator());
291291
return failure.diagnose(asNote);
292292
}
293+
294+
case RefKind::NonConstMetatype: {
295+
ImplicitInitOnNonConstMetatypeFailure failure(root, getConstraintSystem(),
296+
BaseType, Init, getLocator());
297+
return failure.diagnose(asNote);
298+
}
293299
}
294300
}
295301

@@ -308,6 +314,14 @@ AllowInvalidInitRef *AllowInvalidInitRef::onProtocolMetatype(
308314
isStaticallyDerived, baseRange, locator);
309315
}
310316

317+
AllowInvalidInitRef *
318+
AllowInvalidInitRef::onNonConstMetatype(ConstraintSystem &cs, Type baseTy,
319+
ConstructorDecl *init,
320+
ConstraintLocator *locator) {
321+
return create(RefKind::NonConstMetatype, cs, baseTy, init,
322+
/*isStaticallyDerived=*/false, SourceRange(), locator);
323+
}
324+
311325
AllowInvalidInitRef *
312326
AllowInvalidInitRef::create(RefKind kind, ConstraintSystem &cs, Type baseTy,
313327
ConstructorDecl *init, bool isStaticallyDerived,

lib/Sema/CSFix.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,11 @@ class AllowInvalidPartialApplication final : public ConstraintFix {
543543
};
544544

545545
class AllowInvalidInitRef final : public ConstraintFix {
546-
enum class RefKind { DynamicOnMetatype, ProtocolMetatype } Kind;
546+
enum class RefKind {
547+
DynamicOnMetatype,
548+
ProtocolMetatype,
549+
NonConstMetatype,
550+
} Kind;
547551

548552
Type BaseType;
549553
const ConstructorDecl *Init;
@@ -573,6 +577,11 @@ class AllowInvalidInitRef final : public ConstraintFix {
573577
bool isStaticallyDerived, SourceRange baseRange,
574578
ConstraintLocator *locator);
575579

580+
static AllowInvalidInitRef *onNonConstMetatype(ConstraintSystem &cs,
581+
Type baseTy,
582+
ConstructorDecl *init,
583+
ConstraintLocator *locator);
584+
576585
private:
577586
static AllowInvalidInitRef *create(RefKind kind, ConstraintSystem &cs,
578587
Type baseTy, ConstructorDecl *init,

lib/Sema/ConstraintSystem.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,6 +1752,16 @@ static void validateInitializerRef(ConstraintSystem &cs, ConstructorDecl *init,
17521752
} else if (auto *CE = dyn_cast<CallExpr>(anchor)) {
17531753
baseExpr = CE->getFn();
17541754
baseType = getType(baseExpr);
1755+
// If this is an initializer call without explicit mention
1756+
// of `.init` on metatype value.
1757+
if (auto *MTT = baseType->getAs<MetatypeType>()) {
1758+
auto instanceType = MTT->getInstanceType();
1759+
if (!cs.isTypeReference(baseExpr) && !instanceType->isExistentialType()) {
1760+
(void)cs.recordFix(AllowInvalidInitRef::onNonConstMetatype(
1761+
cs, baseType, init, locator));
1762+
return;
1763+
}
1764+
}
17551765
// Initializer reference which requires contextual base type e.g. `.init(...)`.
17561766
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
17571767
// We need to find type variable which represents contextual base.

test/Constraints/construction.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,8 @@ SR_5245(s: SR_5245.S(f: [.e1, .e2]))
172172

173173
// rdar://problem/34670592 - Compiler crash on heterogeneous collection literal
174174
_ = Array([1, "hello"]) // Ok
175+
176+
func init_via_non_const_metatype(_ s1: S1.Type) {
177+
_ = s1(i: 42) // expected-error {{initializing from a metatype value must reference 'init' explicitly}} {{9-9=.init}}
178+
_ = s1.init(i: 42) // ok
179+
}

0 commit comments

Comments
 (0)