mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Move checking of non-failable-to-failable initializer calls into constraint application.
This makes sure we get the same checking for initializer delegation in structs/enums as we do for classes, fixing rdar://problem/18458622. Swift SVN r23128
This commit is contained in:
@@ -306,7 +306,8 @@ namespace {
|
||||
ConstraintSystem &cs;
|
||||
DeclContext *dc;
|
||||
const Solution &solution;
|
||||
|
||||
bool SuppressDiagnostics;
|
||||
|
||||
private:
|
||||
/// \brief Coerce the given tuple to another tuple type.
|
||||
///
|
||||
@@ -1113,6 +1114,7 @@ namespace {
|
||||
/// \brief Build a new reference to another constructor.
|
||||
Expr *buildOtherConstructorRef(Type openedFullType,
|
||||
ConstructorDecl *ctor, SourceLoc loc,
|
||||
bool isDelegating,
|
||||
bool implicit) {
|
||||
auto &tc = cs.getTypeChecker();
|
||||
auto &ctx = tc.Context;
|
||||
@@ -1148,6 +1150,28 @@ namespace {
|
||||
// Build the constructor reference.
|
||||
Expr *refExpr = new (ctx) OtherConstructorDeclRefExpr(ref, loc, implicit,
|
||||
resultTy);
|
||||
|
||||
// A non-failable initializer cannot delegate to a failable
|
||||
// initializer.
|
||||
if (auto inCtor = dyn_cast<ConstructorDecl>(cs.DC)) {
|
||||
if (ctor->getFailability() == OTK_Optional &&
|
||||
inCtor->getFailability() == OTK_None) {
|
||||
// If we're suppressing diagnostics, just fail.
|
||||
if (SuppressDiagnostics)
|
||||
return nullptr;
|
||||
|
||||
// Note: we can't actually patch up the AST here, because we
|
||||
// might have already type-checked calls to this initializer, so
|
||||
// put the Fix-It on a note.
|
||||
tc.diagnose(loc, diag::delegate_chain_nonoptional_to_optional,
|
||||
!isDelegating, ctor->getFullName());
|
||||
tc.diagnose(inCtor->getLoc(), diag::init_propagate_failure)
|
||||
.fixItInsert(Lexer::getLocForEndOfToken(ctx.SourceMgr,
|
||||
inCtor->getLoc()),
|
||||
"?");
|
||||
}
|
||||
}
|
||||
|
||||
return refExpr;
|
||||
}
|
||||
|
||||
@@ -1269,8 +1293,10 @@ namespace {
|
||||
TypeAliasDecl *MaxFloatTypeDecl = nullptr;
|
||||
|
||||
public:
|
||||
ExprRewriter(ConstraintSystem &cs, const Solution &solution)
|
||||
: cs(cs), dc(cs.DC), solution(solution) { }
|
||||
ExprRewriter(ConstraintSystem &cs, const Solution &solution,
|
||||
bool suppressDiagnostics)
|
||||
: cs(cs), dc(cs.DC), solution(solution),
|
||||
SuppressDiagnostics(suppressDiagnostics) { }
|
||||
|
||||
~ExprRewriter() {
|
||||
finalize();
|
||||
@@ -1939,8 +1965,9 @@ namespace {
|
||||
// Make sure the reference to 'self' occurs within an initializer.
|
||||
if (!dyn_cast_or_null<ConstructorDecl>(
|
||||
cs.DC->getInnermostMethodContext())) {
|
||||
tc.diagnose(expr->getDotLoc(),
|
||||
diag::init_delegation_outside_initializer);
|
||||
if (!SuppressDiagnostics)
|
||||
tc.diagnose(expr->getDotLoc(),
|
||||
diag::init_delegation_outside_initializer);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -1959,6 +1986,9 @@ namespace {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SuppressDiagnostics)
|
||||
return nullptr;
|
||||
|
||||
tc.diagnose(expr->getDotLoc(), diag::bad_init_ref_base, hasSuper);
|
||||
}
|
||||
@@ -1971,9 +2001,11 @@ namespace {
|
||||
diagnoseIfOverloadChoiceUnavailable(choice, expr->getConstructorLoc());
|
||||
|
||||
// Build a partial application of the initializer.
|
||||
Expr *ctorRef = buildOtherConstructorRef(selected.openedFullType,
|
||||
ctor, expr->getConstructorLoc(),
|
||||
expr->isImplicit());
|
||||
Expr *ctorRef = buildOtherConstructorRef(
|
||||
selected.openedFullType,
|
||||
ctor, expr->getConstructorLoc(),
|
||||
!expr->getSubExpr()->isSuperExpr(),
|
||||
expr->isImplicit());
|
||||
auto *call
|
||||
= new (cs.getASTContext()) DotSyntaxCallExpr(ctorRef,
|
||||
expr->getDotLoc(),
|
||||
@@ -2700,6 +2732,9 @@ namespace {
|
||||
destObjectType = metaTy->getInstanceType();
|
||||
if (auto destClass = destObjectType->getClassOrBoundGenericClass()) {
|
||||
if (destClass->isForeign()) {
|
||||
if (SuppressDiagnostics)
|
||||
return nullptr;
|
||||
|
||||
tc.diagnose(cast->getLoc(), diag::conditional_downcast_foreign,
|
||||
destValueType);
|
||||
}
|
||||
@@ -2901,6 +2936,9 @@ namespace {
|
||||
case CheckedCastKind::Unresolved:
|
||||
return nullptr;
|
||||
case CheckedCastKind::Coercion: {
|
||||
if (SuppressDiagnostics)
|
||||
return nullptr;
|
||||
|
||||
tc.diagnose(expr->getLoc(), diag::conditional_downcast_coercion,
|
||||
sub->getType(), toType);
|
||||
|
||||
@@ -4706,6 +4744,9 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
|
||||
!fn->isStaticallyDerivedMetatype() &&
|
||||
!decl->hasClangNode() &&
|
||||
!cast<ConstructorDecl>(decl)->isRequired()) {
|
||||
if (SuppressDiagnostics)
|
||||
return nullptr;
|
||||
|
||||
tc.diagnose(apply->getLoc(), diag::dynamic_construct_class, ty)
|
||||
.highlight(fn->getSourceRange());
|
||||
auto ctor = cast<ConstructorDecl>(decl);
|
||||
@@ -4885,7 +4926,8 @@ static bool diagnoseRelabel(TypeChecker &tc, Expr *expr,
|
||||
|
||||
/// \brief Apply a given solution to the expression, producing a fully
|
||||
/// type-checked expression.
|
||||
Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr) {
|
||||
Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr,
|
||||
bool suppressDiagnostics) {
|
||||
// If any fixes needed to be applied to arrive at this solution, resolve
|
||||
// them to specific expressions.
|
||||
if (!solution.Fixes.empty()) {
|
||||
@@ -5245,15 +5287,16 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr) {
|
||||
bool walkToDeclPre(Decl *decl) override { return false; }
|
||||
};
|
||||
|
||||
ExprRewriter rewriter(*this, solution);
|
||||
ExprRewriter rewriter(*this, solution, suppressDiagnostics);
|
||||
ExprWalker walker(rewriter);
|
||||
auto result = expr->walk(walker);
|
||||
return result;
|
||||
}
|
||||
|
||||
Expr *ConstraintSystem::applySolutionShallow(const Solution &solution,
|
||||
Expr *expr) {
|
||||
ExprRewriter rewriter(*this, solution);
|
||||
Expr *expr,
|
||||
bool suppressDiagnostics) {
|
||||
ExprRewriter rewriter(*this, solution, suppressDiagnostics);
|
||||
return rewriter.visit(expr);
|
||||
}
|
||||
|
||||
@@ -5261,7 +5304,7 @@ Expr *Solution::coerceToType(Expr *expr, Type toType,
|
||||
ConstraintLocator *locator,
|
||||
bool ignoreTopLevelInjection) const {
|
||||
auto &cs = getConstraintSystem();
|
||||
ExprRewriter rewriter(cs, *this);
|
||||
ExprRewriter rewriter(cs, *this, /*suppressDiagnostics=*/false);
|
||||
Expr *result = rewriter.coerceToType(expr, toType, locator);
|
||||
if (!result)
|
||||
return nullptr;
|
||||
@@ -5386,7 +5429,7 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc,
|
||||
}
|
||||
|
||||
Solution &solution = solutions.front();
|
||||
ExprRewriter rewriter(cs, solution);
|
||||
ExprRewriter rewriter(cs, solution, /*suppressDiagnostics=*/false);
|
||||
|
||||
auto memberRef = rewriter.buildMemberRef(base, openedFullType,
|
||||
base->getStartLoc(),
|
||||
@@ -5428,7 +5471,7 @@ static Expr *convertViaBuiltinProtocol(const Solution &solution,
|
||||
Diag<> brokenProtocolDiag,
|
||||
Diag<> brokenBuiltinDiag) {
|
||||
auto &cs = solution.getConstraintSystem();
|
||||
ExprRewriter rewriter(cs, solution);
|
||||
ExprRewriter rewriter(cs, solution, /*suppressDiagnostics=*/false);
|
||||
|
||||
// FIXME: Cache name.
|
||||
auto &tc = cs.getTypeChecker();
|
||||
|
||||
@@ -2159,11 +2159,13 @@ public:
|
||||
|
||||
/// \brief Apply a given solution to the expression, producing a fully
|
||||
/// type-checked expression.
|
||||
Expr *applySolution(Solution &solution, Expr *expr);
|
||||
Expr *applySolution(Solution &solution, Expr *expr,
|
||||
bool suppressDiagnostics);
|
||||
|
||||
/// \brief Apply a given solution to the expression to the top-level
|
||||
/// expression, producing a fully type-checked expression.
|
||||
Expr *applySolutionShallow(const Solution &solution, Expr *expr);
|
||||
Expr *applySolutionShallow(const Solution &solution, Expr *expr,
|
||||
bool suppressDiagnostics);
|
||||
|
||||
/// \brief Obtain the specializations computed for a type variable. This is
|
||||
/// useful when emitting diagnostics for computed type variables.
|
||||
|
||||
@@ -963,7 +963,8 @@ bool TypeChecker::typeCheckExpression(
|
||||
}
|
||||
|
||||
// Apply the solution to the expression.
|
||||
auto result = cs.applySolution(solution, expr);
|
||||
auto result = cs.applySolution(solution, expr,
|
||||
listener && listener->suppressDiagnostics());
|
||||
if (!result) {
|
||||
diagnoseExpr(*this, expr, dc, listener);
|
||||
// Failure already diagnosed, above, as part of applying the solution.
|
||||
@@ -1059,7 +1060,8 @@ bool TypeChecker::typeCheckExpressionShallow(Expr *&expr, DeclContext *dc,
|
||||
}
|
||||
|
||||
// Apply the solution to the expression.
|
||||
auto result = cs.applySolutionShallow(solution, expr);
|
||||
auto result = cs.applySolutionShallow(solution, expr,
|
||||
/*suppressDiagnostics=*/false);
|
||||
if (!result) {
|
||||
// Failure already diagnosed, above, as part of applying the solution.
|
||||
return true;
|
||||
|
||||
@@ -890,52 +890,6 @@ Expr* TypeChecker::constructCallToSuperInit(ConstructorDecl *ctor,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Check failability of one initializer calling another.
|
||||
static bool checkInitializerFailability(TypeChecker &tc, SourceLoc loc,
|
||||
ConstructorDecl *fromCtor,
|
||||
ConstructorDecl *toCtor,
|
||||
bool isDelegating,
|
||||
bool implicitlyGenerated) {
|
||||
if (toCtor->getFailability() == OTK_Optional &&
|
||||
fromCtor->getFailability() == OTK_None) {
|
||||
if (!implicitlyGenerated) {
|
||||
// Note: we can't actually patch up the AST here, because we
|
||||
// might have already type-checked calls to this initializer, so
|
||||
// put the Fix-It on a note.
|
||||
tc.diagnose(loc, diag::delegate_chain_nonoptional_to_optional,
|
||||
!isDelegating, toCtor->getFullName());
|
||||
tc.diagnose(fromCtor->getLoc(), diag::init_propagate_failure)
|
||||
.fixItInsert(Lexer::getLocForEndOfToken(tc.Context.SourceMgr,
|
||||
fromCtor->getLoc()),
|
||||
"?");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Check a self.init call.
|
||||
///
|
||||
/// \returns true if an error occurred.
|
||||
static bool checkDelegatingInit(TypeChecker &tc, ConstructorDecl *fromCtor,
|
||||
ApplyExpr *apply) {
|
||||
// Make sure we are referring to a designated initializer.
|
||||
auto otherCtorRef = dyn_cast<OtherConstructorDeclRefExpr>(
|
||||
apply->getFn()->getSemanticsProvidingExpr());
|
||||
if (!otherCtorRef)
|
||||
return false;
|
||||
|
||||
// If the target initializer is failable (using '?'), and the current
|
||||
// initializer is not, complain.
|
||||
auto ctor = otherCtorRef->getDecl();
|
||||
return checkInitializerFailability(tc, apply->getFn()->getLoc(),
|
||||
fromCtor, ctor,
|
||||
/*isDelegating=*/true,
|
||||
/*implicitlyGenerated=*/false);
|
||||
}
|
||||
|
||||
/// Check a super.init call.
|
||||
///
|
||||
/// \returns true if an error occurred.
|
||||
@@ -957,14 +911,6 @@ static bool checkSuperInit(TypeChecker &tc, ConstructorDecl *fromCtor,
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the superclass initializer is failable (using '?'), and the current
|
||||
// initializer is not, complain.
|
||||
if (checkInitializerFailability(tc, apply->getArg()->getLoc(),
|
||||
fromCtor, ctor, /*isDelegating=*/false,
|
||||
implicitlyGenerated)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For an implicitly generated super.init() call, make sure there's
|
||||
// only one designated initializer.
|
||||
if (implicitlyGenerated) {
|
||||
@@ -1015,8 +961,6 @@ bool TypeChecker::typeCheckConstructorBodyUntil(ConstructorDecl *ctor,
|
||||
case ConstructorDecl::BodyInitKind::Delegating:
|
||||
isDelegating = true;
|
||||
wantSuperInitCall = false;
|
||||
if (initExpr)
|
||||
checkDelegatingInit(*this, ctor, initExpr);
|
||||
break;
|
||||
|
||||
case ConstructorDecl::BodyInitKind::Chained:
|
||||
|
||||
@@ -123,6 +123,14 @@ extension Super {
|
||||
}
|
||||
}
|
||||
|
||||
struct SomeStruct {
|
||||
init(nonFail: Int) { // expected-note{{propagate the failure with 'init?'}}{{8-8=?}}
|
||||
self.init(fail: nonFail) // expected-error{{a non-failable initializer cannot delegate to failable initializer 'init(fail:)' written with 'init?'}}
|
||||
}
|
||||
|
||||
init?(fail: Int) {}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Initializer overriding
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user