Skip to content

Commit b73b7c2

Browse files
authored
Merge pull request swiftlang#30592 from xedin/fallback-diag-for-holes
[ConstraintSystem] Avoid applying invalid solution with fixes
2 parents 440acea + 5f328ad commit b73b7c2

15 files changed

+91
-76
lines changed

lib/Sema/CSApply.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8025,6 +8025,17 @@ Optional<SolutionApplicationTarget> ConstraintSystem::applySolution(
80258025
}
80268026
}
80278027

8028+
// If there are no fixes recorded but score indicates that there
8029+
// should have been at least one, let's fail application and
8030+
// produce a fallback diagnostic to highlight the problem.
8031+
{
8032+
const auto &score = solution.getFixedScore();
8033+
if (score.Data[SK_Fix] > 0 || score.Data[SK_Hole] > 0) {
8034+
maybeProduceFallbackDiagnostic(target);
8035+
return None;
8036+
}
8037+
}
8038+
80288039
ExprRewriter rewriter(*this, solution, shouldSuppressDiagnostics());
80298040
ExprWalker walker(rewriter);
80308041
auto resultTarget = walker.rewriteTarget(target);

lib/Sema/CSBindings.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,10 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
10861086
cs.DefaultedConstraints.push_back(srcLocator);
10871087

10881088
if (type->isHole()) {
1089+
// Reflect in the score that this type variable couldn't be
1090+
// resolved and had to be bound to a placeholder "hole" type.
1091+
cs.increaseScore(SK_Hole);
1092+
10891093
if (auto *GP = TypeVar->getImpl().getGenericParameter()) {
10901094
auto path = dstLocator->getPath();
10911095
// Drop `generic parameter` locator element so that all missing

lib/Sema/CSGen.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,9 @@ namespace {
931931
= { nullptr, nullptr };
932932
unsigned currentEditorPlaceholderVariable = 0;
933933

934+
/// Keep track of acceptable DiscardAssignmentExpr's.
935+
llvm::SmallPtrSet<DiscardAssignmentExpr*, 2> CorrectDiscardAssignmentExprs;
936+
934937
/// Returns false and emits the specified diagnostic if the member reference
935938
/// base is a nil literal. Returns true otherwise.
936939
bool isValidBaseOfMemberRef(Expr *base, Diag<> diagnostic) {
@@ -3057,9 +3060,15 @@ namespace {
30573060
}
30583061

30593062
Type visitDiscardAssignmentExpr(DiscardAssignmentExpr *expr) {
3063+
/// Diagnose a '_' that isn't on the immediate LHS of an assignment.
3064+
if (!CorrectDiscardAssignmentExprs.count(expr)) {
3065+
auto &DE = CS.getASTContext().Diags;
3066+
DE.diagnose(expr->getLoc(), diag::discard_expr_outside_of_assignment);
3067+
return Type();
3068+
}
3069+
30603070
auto locator = CS.getConstraintLocator(expr);
3061-
auto typeVar = CS.createTypeVariable(locator, TVO_CanBindToNoEscape |
3062-
TVO_CanBindToHole);
3071+
auto typeVar = CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
30633072
return LValueType::get(typeVar);
30643073
}
30653074

@@ -3095,6 +3104,25 @@ namespace {
30953104
}
30963105
}
30973106

3107+
/// Scout out the specified destination of an AssignExpr to recursively
3108+
/// identify DiscardAssignmentExpr in legal places. We can only allow them
3109+
/// in simple pattern-like expressions, so we reject anything complex here.
3110+
void markAcceptableDiscardExprs(Expr *E) {
3111+
if (!E) return;
3112+
3113+
if (auto *PE = dyn_cast<ParenExpr>(E))
3114+
return markAcceptableDiscardExprs(PE->getSubExpr());
3115+
if (auto *TE = dyn_cast<TupleExpr>(E)) {
3116+
for (auto &elt : TE->getElements())
3117+
markAcceptableDiscardExprs(elt);
3118+
return;
3119+
}
3120+
if (auto *DAE = dyn_cast<DiscardAssignmentExpr>(E))
3121+
CorrectDiscardAssignmentExprs.insert(DAE);
3122+
3123+
// Otherwise, we can't support this.
3124+
}
3125+
30983126
Type visitAssignExpr(AssignExpr *expr) {
30993127
// Handle invalid code.
31003128
if (!expr->getDest() || !expr->getSrc())
@@ -3924,6 +3952,9 @@ namespace {
39243952
return { false, expr };
39253953
}
39263954

3955+
if (auto *assignment = dyn_cast<AssignExpr>(expr))
3956+
CG.markAcceptableDiscardExprs(assignment->getDest());
3957+
39273958
return { true, expr };
39283959
}
39293960

lib/Sema/CSRanking.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) {
4141
log.indent(solverState->depth * 2);
4242
log << "(increasing score due to ";
4343
switch (kind) {
44+
case SK_Hole:
45+
log << "hole in the constraint system";
46+
break;
47+
4448
case SK_Unavailable:
4549
log << "use of an unavailable declaration";
4650
break;

lib/Sema/CSSimplify.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5073,7 +5073,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
50735073
// func foo<T: BinaryInteger>(_: T) {}
50745074
// foo(Foo.bar) <- if `Foo` doesn't have `bar` there is
50755075
// no reason to complain about missing conformance.
5076-
increaseScore(SK_Fix);
50775076
return SolutionKind::Solved;
50785077
}
50795078

@@ -6466,7 +6465,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
64666465
baseObjTy->isHole()) {
64676466
// If base type is a "hole" there is no reason to record any
64686467
// more "member not found" fixes for chained member references.
6469-
increaseScore(SK_Fix);
64706468
markMemberTypeAsPotentialHole(memberTy);
64716469
return SolutionKind::Solved;
64726470
}

lib/Sema/CSSolver.cpp

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,23 +1139,6 @@ static bool debugConstraintSolverForTarget(
11391139
return startBound != endBound;
11401140
}
11411141

1142-
/// If we aren't certain that we've emitted a diagnostic, emit a fallback
1143-
/// diagnostic.
1144-
static void maybeProduceFallbackDiagnostic(
1145-
ConstraintSystem &cs, SolutionApplicationTarget target) {
1146-
if (cs.Options.contains(ConstraintSystemFlags::SuppressDiagnostics))
1147-
return;
1148-
1149-
// Before producing fatal error here, let's check if there are any "error"
1150-
// diagnostics already emitted or waiting to be emitted. Because they are
1151-
// a better indication of the problem.
1152-
ASTContext &ctx = cs.getASTContext();
1153-
if (ctx.Diags.hadAnyError() || ctx.hasDelayedConformanceErrors())
1154-
return;
1155-
1156-
ctx.Diags.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic);
1157-
}
1158-
11591142
Optional<std::vector<Solution>> ConstraintSystem::solve(
11601143
SolutionApplicationTarget &target,
11611144
ExprTypeCheckListener *listener,
@@ -1201,7 +1184,7 @@ Optional<std::vector<Solution>> ConstraintSystem::solve(
12011184
}
12021185

12031186
case SolutionResult::Error:
1204-
maybeProduceFallbackDiagnostic(*this, target);
1187+
maybeProduceFallbackDiagnostic(target);
12051188
return None;
12061189

12071190
case SolutionResult::TooComplex:

lib/Sema/ConstraintSystem.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4387,3 +4387,20 @@ bool ConstraintSystem::isDeclUnavailable(const Decl *D,
43874387
AvailabilityContext result = AvailabilityContext::alwaysAvailable();
43884388
return !TypeChecker::isDeclAvailable(D, loc, DC, result);
43894389
}
4390+
4391+
/// If we aren't certain that we've emitted a diagnostic, emit a fallback
4392+
/// diagnostic.
4393+
void ConstraintSystem::maybeProduceFallbackDiagnostic(
4394+
SolutionApplicationTarget target) const {
4395+
if (Options.contains(ConstraintSystemFlags::SuppressDiagnostics))
4396+
return;
4397+
4398+
// Before producing fatal error here, let's check if there are any "error"
4399+
// diagnostics already emitted or waiting to be emitted. Because they are
4400+
// a better indication of the problem.
4401+
ASTContext &ctx = getASTContext();
4402+
if (ctx.Diags.hadAnyError() || ctx.hasDelayedConformanceErrors())
4403+
return;
4404+
4405+
ctx.Diags.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic);
4406+
}

lib/Sema/ConstraintSystem.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,8 @@ enum ScoreKind {
634634

635635
/// A fix needs to be applied to the source.
636636
SK_Fix,
637+
/// A hole in the constraint system.
638+
SK_Hole,
637639
/// A reference to an @unavailable declaration.
638640
SK_Unavailable,
639641
/// A use of a disfavored overload.
@@ -4643,6 +4645,10 @@ class ConstraintSystem {
46434645
SmallVectorImpl<unsigned> &Ordering,
46444646
SmallVectorImpl<unsigned> &PartitionBeginning);
46454647

4648+
/// If we aren't certain that we've emitted a diagnostic, emit a fallback
4649+
/// diagnostic.
4650+
void maybeProduceFallbackDiagnostic(SolutionApplicationTarget target) const;
4651+
46464652
SWIFT_DEBUG_DUMP;
46474653
SWIFT_DEBUG_DUMPER(dump(Expr *));
46484654

lib/Sema/MiscDiagnostics.cpp

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
6868
SmallPtrSet<Expr*, 4> AlreadyDiagnosedMetatypes;
6969
SmallPtrSet<DeclRefExpr*, 4> AlreadyDiagnosedBitCasts;
7070

71-
/// Keep track of acceptable DiscardAssignmentExpr's.
72-
SmallPtrSet<DiscardAssignmentExpr*, 2> CorrectDiscardAssignmentExprs;
73-
7471
/// Keep track of the arguments to CallExprs.
7572
SmallPtrSet<Expr *, 2> CallArgs;
7673

@@ -229,7 +226,6 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
229226
// If we have an assignment expression, scout ahead for acceptable _'s.
230227
if (auto *AE = dyn_cast<AssignExpr>(E)) {
231228
auto destExpr = AE->getDest();
232-
markAcceptableDiscardExprs(destExpr);
233229
// If the user is assigning the result of a function that returns
234230
// Void to _ then warn, because that is redundant.
235231
if (auto DAE = dyn_cast<DiscardAssignmentExpr>(destExpr)) {
@@ -246,14 +242,6 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
246242
}
247243
}
248244

249-
/// Diagnose a '_' that isn't on the immediate LHS of an assignment.
250-
if (auto *DAE = dyn_cast<DiscardAssignmentExpr>(E)) {
251-
if (!CorrectDiscardAssignmentExprs.count(DAE) &&
252-
!DAE->getType()->hasError())
253-
Ctx.Diags.diagnose(DAE->getLoc(),
254-
diag::discard_expr_outside_of_assignment);
255-
}
256-
257245
// Diagnose 'self.init' or 'super.init' nested in another expression
258246
// or closure.
259247
if (auto *rebindSelfExpr = dyn_cast<RebindSelfInConstructorExpr>(E)) {
@@ -425,26 +413,6 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
425413
}
426414
}
427415

428-
429-
/// Scout out the specified destination of an AssignExpr to recursively
430-
/// identify DiscardAssignmentExpr in legal places. We can only allow them
431-
/// in simple pattern-like expressions, so we reject anything complex here.
432-
void markAcceptableDiscardExprs(Expr *E) {
433-
if (!E) return;
434-
435-
if (auto *PE = dyn_cast<ParenExpr>(E))
436-
return markAcceptableDiscardExprs(PE->getSubExpr());
437-
if (auto *TE = dyn_cast<TupleExpr>(E)) {
438-
for (auto &elt : TE->getElements())
439-
markAcceptableDiscardExprs(elt);
440-
return;
441-
}
442-
if (auto *DAE = dyn_cast<DiscardAssignmentExpr>(E))
443-
CorrectDiscardAssignmentExprs.insert(DAE);
444-
445-
// Otherwise, we can't support this.
446-
}
447-
448416
void checkMagicIdentifierMismatch(ConcreteDeclRef callee,
449417
unsigned uncurryLevel,
450418
unsigned argIndex,

test/Constraints/one_way_solve.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ func testTernaryOneWayOverload(b: Bool) {
3838

3939
// CHECK: solving component #1
4040
// CHECK: Initial bindings: $T11 := Int8
41-
// CHECK: found solution 0 0 0 0 0 0 2 0 0 0 0 0
41+
// CHECK: found solution 0 0 0 0 0 0 0 2 0 0 0 0 0
4242

43-
// CHECK: composed solution 0 0 0 0 0 0 2 0 0 0 0 0
44-
// CHECK-NOT: composed solution 0 0 0 0 0 0 2 0 0 0 0 0
43+
// CHECK: composed solution 0 0 0 0 0 0 0 2 0 0 0 0 0
44+
// CHECK-NOT: composed solution 0 0 0 0 0 0 0 2 0 0 0 0 0
4545
let _: Int8 = b ? Builtin.one_way(int8Or16(17)) : Builtin.one_way(int8Or16(42))
4646
}

0 commit comments

Comments
 (0)