Skip to content

Commit 188b87d

Browse files
authored
Merge pull request #37138 from xedin/disable-only-in-perf-5.5
[5.5][CSSimplify] Allow overload choices with missing labels to be considered for diagnostics
2 parents b08d3bb + 21e6bbb commit 188b87d

File tree

13 files changed

+131
-46
lines changed

13 files changed

+131
-46
lines changed

include/swift/Sema/Constraint.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ class Constraint final : public llvm::ilist_node<Constraint>,
324324
/// constraint graph during constraint propagation?
325325
unsigned IsDisabled : 1;
326326

327+
/// Constraint is disabled in performance mode only, could be attempted
328+
/// for diagnostic purposes.
329+
unsigned IsDisabledForPerformance : 1;
330+
327331
/// Whether the choice of this disjunction should be recorded in the
328332
/// solver state.
329333
unsigned RememberChoice : 1;
@@ -542,18 +546,27 @@ class Constraint final : public llvm::ilist_node<Constraint>,
542546
IsActive = active;
543547
}
544548

545-
/// Whether this constraint is active, i.e., in the worklist.
546-
bool isDisabled() const { return IsDisabled; }
549+
/// Whether this constraint is disabled and shouldn't be attempted by the
550+
/// solver.
551+
bool isDisabled() const { return IsDisabled || IsDisabledForPerformance; }
552+
553+
/// Whether this constraint is disabled and shouldn't be attempted by the
554+
/// solver only in "performance" mode.
555+
bool isDisabledInPerformanceMode() const { return IsDisabledForPerformance; }
547556

548557
/// Set whether this constraint is active or not.
549-
void setDisabled() {
558+
void setDisabled(bool enableForDiagnostics = false) {
550559
assert(!isActive() && "Cannot disable constraint marked as active!");
551-
IsDisabled = true;
560+
if (enableForDiagnostics)
561+
IsDisabledForPerformance = true;
562+
else
563+
IsDisabled = true;
552564
}
553565

554566
void setEnabled() {
555567
assert(isDisabled() && "Can't re-enable already active constraint!");
556568
IsDisabled = false;
569+
IsDisabledForPerformance = false;
557570
}
558571

559572
/// Mark or retrieve whether this constraint should be favored in the system.

include/swift/Sema/ConstraintSystem.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5304,7 +5304,20 @@ class DisjunctionChoice {
53045304

53055305
bool attempt(ConstraintSystem &cs) const;
53065306

5307-
bool isDisabled() const { return Choice->isDisabled(); }
5307+
bool isDisabled() const {
5308+
if (!Choice->isDisabled())
5309+
return false;
5310+
5311+
// If solver is in a diagnostic mode, let's allow
5312+
// constraints that have fixes or have been disabled
5313+
// in attempt to produce a solution faster for
5314+
// well-formed expressions.
5315+
if (CS.shouldAttemptFixes()) {
5316+
return !(hasFix() || Choice->isDisabledInPerformanceMode());
5317+
}
5318+
5319+
return true;
5320+
}
53085321

53095322
bool hasFix() const {
53105323
return bool(Choice->getFix());

lib/Sema/CSSimplify.cpp

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ bool constraints::doesMemberRefApplyCurriedSelf(Type baseTy,
125125

126126
static bool areConservativelyCompatibleArgumentLabels(
127127
OverloadChoice choice, SmallVectorImpl<FunctionType::Param> &args,
128+
MatchCallArgumentListener &listener,
128129
Optional<unsigned> unlabeledTrailingClosureArgIndex) {
129130
ValueDecl *decl = nullptr;
130131
switch (choice.getKind()) {
@@ -163,7 +164,6 @@ static bool areConservativelyCompatibleArgumentLabels(
163164
auto params = fnType->getParams();
164165
ParameterListInfo paramInfo(params, decl, hasAppliedSelf);
165166

166-
MatchCallArgumentListener listener;
167167
return matchCallArguments(args, params, paramInfo,
168168
unlabeledTrailingClosureArgIndex,
169169
/*allow fixes*/ false, listener,
@@ -1220,6 +1220,32 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
12201220
}
12211221
};
12221222

1223+
class AllowLabelMismatches : public MatchCallArgumentListener {
1224+
SmallVector<Identifier, 4> NewLabels;
1225+
bool HadLabelingIssues = false;
1226+
1227+
public:
1228+
bool missingLabel(unsigned paramIndex) override {
1229+
HadLabelingIssues = true;
1230+
return false;
1231+
}
1232+
1233+
bool relabelArguments(ArrayRef<Identifier> newLabels) override {
1234+
NewLabels.append(newLabels.begin(), newLabels.end());
1235+
HadLabelingIssues = true;
1236+
return false;
1237+
}
1238+
1239+
bool hadLabelingIssues() const { return HadLabelingIssues; }
1240+
1241+
Optional<ArrayRef<Identifier>> getLabelReplacements() const {
1242+
if (!hadLabelingIssues() || NewLabels.empty())
1243+
return None;
1244+
1245+
return {NewLabels};
1246+
}
1247+
};
1248+
12231249
// Match the argument of a call to the parameter.
12241250
ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
12251251
ConstraintSystem &cs, FunctionType *contextualType,
@@ -9724,11 +9750,57 @@ bool ConstraintSystem::simplifyAppliedOverloadsImpl(
97249750
argsWithLabels.append(args.begin(), args.end());
97259751
FunctionType::relabelParams(argsWithLabels, argumentInfo->Labels);
97269752

9727-
if (!areConservativelyCompatibleArgumentLabels(
9728-
choice, argsWithLabels,
9729-
argumentInfo->UnlabeledTrailingClosureIndex)) {
9753+
auto labelsMatch = [&](MatchCallArgumentListener &listener) {
9754+
if (areConservativelyCompatibleArgumentLabels(
9755+
choice, argsWithLabels, listener,
9756+
argumentInfo->UnlabeledTrailingClosureIndex))
9757+
return true;
9758+
97309759
labelMismatch = true;
97319760
return false;
9761+
};
9762+
9763+
AllowLabelMismatches listener;
9764+
9765+
// This overload has more problems than just missing/invalid labels.
9766+
if (!labelsMatch(listener))
9767+
return false;
9768+
9769+
// If overload did match, let's check if it needs to be disabled
9770+
// in "performance" mode because it has missing labels.
9771+
if (listener.hadLabelingIssues()) {
9772+
// In performance mode, let's just disable the choice,
9773+
// this decision could be rolled back for diagnostics.
9774+
if (!shouldAttemptFixes())
9775+
return false;
9776+
9777+
// Match expected vs. actual to see whether the only kind
9778+
// of problem here is missing label(s).
9779+
auto onlyMissingLabels =
9780+
[&argumentInfo](ArrayRef<Identifier> expectedLabels) {
9781+
auto actualLabels = argumentInfo->Labels;
9782+
if (actualLabels.size() != expectedLabels.size())
9783+
return false;
9784+
9785+
for (unsigned i = 0; i != actualLabels.size(); ++i) {
9786+
auto actual = actualLabels[i];
9787+
auto expected = expectedLabels[i];
9788+
9789+
if (actual.compare(expected) != 0 && !actual.empty())
9790+
return false;
9791+
}
9792+
9793+
return true;
9794+
};
9795+
9796+
auto replacementLabels = listener.getLabelReplacements();
9797+
// Either it's just one argument or all issues are missing labels.
9798+
if (!replacementLabels || onlyMissingLabels(*replacementLabels)) {
9799+
constraint->setDisabled(/*enableForDiagnostics=*/true);
9800+
// Don't include this overload in "common result" computation
9801+
// because it has issues.
9802+
return true;
9803+
}
97329804
}
97339805
}
97349806

lib/Sema/CSStep.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ bool DisjunctionStep::shouldSkip(const DisjunctionChoice &choice) const {
612612

613613
// Skip disabled overloads in the diagnostic mode if they do not have a
614614
// fix attached to them e.g. overloads where labels didn't match up.
615-
if (choice.isDisabled() && !(CS.shouldAttemptFixes() && choice.hasFix()))
615+
if (choice.isDisabled())
616616
return skip("disabled");
617617

618618
// Skip unavailable overloads (unless in dignostic mode).

lib/Sema/Constraint.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Constraint::Constraint(ConstraintKind kind, ArrayRef<Constraint *> constraints,
3030
ConstraintLocator *locator,
3131
SmallPtrSetImpl<TypeVariableType *> &typeVars)
3232
: Kind(kind), HasRestriction(false), IsActive(false), IsDisabled(false),
33-
RememberChoice(false), IsFavored(false),
33+
IsDisabledForPerformance(false), RememberChoice(false), IsFavored(false),
3434
NumTypeVariables(typeVars.size()), Nested(constraints), Locator(locator) {
3535
assert(kind == ConstraintKind::Disjunction);
3636
std::uninitialized_copy(typeVars.begin(), typeVars.end(),
@@ -41,7 +41,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second,
4141
ConstraintLocator *locator,
4242
SmallPtrSetImpl<TypeVariableType *> &typeVars)
4343
: Kind(Kind), HasRestriction(false), IsActive(false), IsDisabled(false),
44-
RememberChoice(false), IsFavored(false),
44+
IsDisabledForPerformance(false), RememberChoice(false), IsFavored(false),
4545
NumTypeVariables(typeVars.size()), Types{First, Second, Type()},
4646
Locator(locator) {
4747
switch (Kind) {
@@ -110,7 +110,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third,
110110
ConstraintLocator *locator,
111111
SmallPtrSetImpl<TypeVariableType *> &typeVars)
112112
: Kind(Kind), HasRestriction(false), IsActive(false), IsDisabled(false),
113-
RememberChoice(false), IsFavored(false),
113+
IsDisabledForPerformance(false), RememberChoice(false), IsFavored(false),
114114
NumTypeVariables(typeVars.size()), Types{First, Second, Third},
115115
Locator(locator) {
116116
switch (Kind) {
@@ -168,7 +168,7 @@ Constraint::Constraint(ConstraintKind kind, Type first, Type second,
168168
ConstraintLocator *locator,
169169
SmallPtrSetImpl<TypeVariableType *> &typeVars)
170170
: Kind(kind), HasRestriction(false), IsActive(false), IsDisabled(false),
171-
RememberChoice(false), IsFavored(false),
171+
IsDisabledForPerformance(false), RememberChoice(false), IsFavored(false),
172172
NumTypeVariables(typeVars.size()), Member{first, second, {member}, useDC},
173173
Locator(locator) {
174174
assert(kind == ConstraintKind::ValueMember ||
@@ -187,7 +187,7 @@ Constraint::Constraint(ConstraintKind kind, Type first, Type second,
187187
ConstraintLocator *locator,
188188
SmallPtrSetImpl<TypeVariableType *> &typeVars)
189189
: Kind(kind), HasRestriction(false), IsActive(false), IsDisabled(false),
190-
RememberChoice(false), IsFavored(false),
190+
IsDisabledForPerformance(false), RememberChoice(false), IsFavored(false),
191191
NumTypeVariables(typeVars.size()), Locator(locator) {
192192
Member.First = first;
193193
Member.Second = second;
@@ -207,8 +207,8 @@ Constraint::Constraint(Type type, OverloadChoice choice, DeclContext *useDC,
207207
ConstraintFix *fix, ConstraintLocator *locator,
208208
SmallPtrSetImpl<TypeVariableType *> &typeVars)
209209
: Kind(ConstraintKind::BindOverload), TheFix(fix), HasRestriction(false),
210-
IsActive(false), IsDisabled(bool(fix)), RememberChoice(false),
211-
IsFavored(false),
210+
IsActive(false), IsDisabled(bool(fix)), IsDisabledForPerformance(false),
211+
RememberChoice(false), IsFavored(false),
212212
NumTypeVariables(typeVars.size()), Overload{type, choice, useDC},
213213
Locator(locator) {
214214
std::copy(typeVars.begin(), typeVars.end(), getTypeVariablesBuffer().begin());
@@ -219,8 +219,8 @@ Constraint::Constraint(ConstraintKind kind,
219219
Type second, ConstraintLocator *locator,
220220
SmallPtrSetImpl<TypeVariableType *> &typeVars)
221221
: Kind(kind), Restriction(restriction), HasRestriction(true),
222-
IsActive(false), IsDisabled(false), RememberChoice(false),
223-
IsFavored(false),
222+
IsActive(false), IsDisabled(false), IsDisabledForPerformance(false),
223+
RememberChoice(false), IsFavored(false),
224224
NumTypeVariables(typeVars.size()), Types{first, second, Type()},
225225
Locator(locator) {
226226
assert(!first.isNull());
@@ -232,7 +232,8 @@ Constraint::Constraint(ConstraintKind kind, ConstraintFix *fix, Type first,
232232
Type second, ConstraintLocator *locator,
233233
SmallPtrSetImpl<TypeVariableType *> &typeVars)
234234
: Kind(kind), TheFix(fix), HasRestriction(false), IsActive(false),
235-
IsDisabled(false), RememberChoice(false), IsFavored(false),
235+
IsDisabled(false), IsDisabledForPerformance(false), RememberChoice(false),
236+
IsFavored(false),
236237
NumTypeVariables(typeVars.size()), Types{first, second, Type()},
237238
Locator(locator) {
238239
assert(!first.isNull());

test/Constraints/diagnostics.swift

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,18 +1102,10 @@ func rdar17170728() {
11021102
// expected-error@-1 4 {{optional type 'Int?' cannot be used as a boolean; test for '!= nil' instead}}
11031103
}
11041104

1105-
let _ = [i, j, k].reduce(0 as Int?) {
1106-
// expected-error@-1 3 {{cannot convert value of type 'Int?' to expected element type 'Bool'}}
1107-
// expected-error@-2 {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}}
1108-
// expected-note@-3 {{coalesce using '??' to provide a default when the optional value contains 'nil'}}
1109-
// expected-note@-4 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
1105+
let _ = [i, j, k].reduce(0 as Int?) { // expected-error {{missing argument label 'into:' in call}}
1106+
// expected-error@-1 {{cannot convert value of type 'Int?' to expected argument type '(inout @escaping (Bool, Bool) -> Bool?, Int?) throws -> ()'}}
11101107
$0 && $1 ? $0 + $1 : ($0 ? $0 : ($1 ? $1 : nil))
1111-
// expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
1112-
// expected-error@-2 {{cannot convert value of type 'Bool?' to closure result type 'Int'}}
1113-
// expected-error@-3 {{result values in '? :' expression have mismatching types 'Int' and 'Bool?'}}
1114-
// expected-error@-4 {{cannot convert value of type 'Bool' to expected argument type 'Int'}}
1115-
// expected-error@-5 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
1116-
// expected-error@-6 {{result values in '? :' expression have mismatching types 'Int' and 'Bool?'}}
1108+
// expected-error@-1 {{binary operator '+' cannot be applied to two 'Bool' operands}}
11171109
}
11181110
}
11191111

test/Constraints/generics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ let arr = [BottleLayout]()
648648
let layout = BottleLayout(count:1)
649649
let ix = arr.firstIndex(of:layout) // expected-error {{referencing instance method 'firstIndex(of:)' on 'Collection' requires that 'BottleLayout' conform to 'Equatable'}}
650650

651-
let _: () -> UInt8 = { .init("a" as Unicode.Scalar) } // expected-error {{initializer 'init(_:)' requires that 'Unicode.Scalar' conform to 'BinaryInteger'}}
651+
let _: () -> UInt8 = { .init("a" as Unicode.Scalar) } // expected-error {{missing argument label 'ascii:' in call}}
652652

653653
// https://bugs.swift.org/browse/SR-9068
654654
func compare<C: Collection, Key: Hashable, Value: Equatable>(c: C)

test/Constraints/overload_filtering_objc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ import Foundation
1919
}
2020

2121
func testOptional(obj: P) {
22-
// CHECK: disabled disjunction term $T2 bound to decl overload_filtering_objc.(file).P.opt(double:)
22+
// CHECK: [disabled] $T2 bound to decl overload_filtering_objc.(file).P.opt(double:)
2323
_ = obj.opt?(1)
2424
}

test/Constraints/result_builder_diags.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ func erroneousSR11350(x: Int) {
249249
if b {
250250
acceptInt(0) { }
251251
}
252-
}).domap(0) // expected-error{{value of type 'Optional<()>' has no member 'domap'}}
252+
}).domap(0) // expected-error{{value of type '()?' has no member 'domap'}}
253253
}
254254
}
255255

test/Sema/keypath_subscript_nolabel.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,7 @@ struct S3 {
1919
subscript(v v: KeyPath<S3, Int>) -> Int { get { 0 } set(newValue) {} }
2020
}
2121
var s3 = S3()
22-
// TODO(diagnostics): This should actually be a diagnostic that correctly identifies that in the presence
23-
// of a missing label, there are two options for resolution: 'keyPath' and 'v:' and to offer the user
24-
// a choice.
25-
// Today, the ExprTypeChecker identifies the disjunction with two of these possibilities, but
26-
// filters out some of the terms based on label mismatch (but not implicit keypath terms, for example).
27-
// It should probably not do that.
28-
s3[\.x] = 10 // expected-error {{missing argument label 'keyPath:' in subscript}} {{4-4=keyPath: }}
22+
s3[\.x] = 10 // expected-error {{missing argument label 'v:' in subscript}} {{4-4=v: }}
2923

3024
struct S4 {
3125
var x : Int = 0

test/stdlib/UnsafePointerDiagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func unsafeRawBufferPointerConversions(
113113
_ = UnsafeRawBufferPointer(start: rp, count: 1)
114114
_ = UnsafeMutableRawBufferPointer(mrbp)
115115
_ = UnsafeRawBufferPointer(mrbp)
116-
_ = UnsafeMutableRawBufferPointer(rbp) // expected-error {{cannot convert value of type 'UnsafeRawBufferPointer' to expected argument type 'UnsafeMutableRawBufferPointer'}}
116+
_ = UnsafeMutableRawBufferPointer(rbp) // expected-error {{missing argument label 'mutating:' in call}}
117117
_ = UnsafeRawBufferPointer(rbp)
118118
_ = UnsafeMutableRawBufferPointer(mbpi)
119119
_ = UnsafeRawBufferPointer(mbpi)

validation-test/Sema/type_checker_crashers_fixed/rdar45470505.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
extension BinaryInteger {
44
init(bytes: [UInt8]) { fatalError() }
55

6-
init<S: Sequence>(bytes: S) where S.Iterator.Element == UInt8 {
6+
init<S: Sequence>(bytes: S) where S.Iterator.Element == UInt8 { // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(bytes:)')}}
77
self.init(bytes // expected-error {{no exact matches in call to initializer}}
88
// expected-note@-1 {{}}
99

validation-test/compiler_crashers_2_fixed/0158-rdar40165062.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// RUN: %target-typecheck-verify-swift
22

3-
struct Foo<T, U> {
3+
struct Foo<T, U> { // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(value:)')}}
44
var value: U
55
func bar() -> Foo<T, U> {
66
return Foo(value)
7-
// expected-error@-1 {{referencing initializer 'init(_:)' on 'Foo' requires the types 'T' and 'U' be equivalent}}
7+
// expected-error@-1 {{no exact matches in call to initializer}}
88
}
99
}
1010

11-
extension Foo where T == U { // expected-note {{where 'T' = 'T', 'U' = 'U'}}
11+
extension Foo where T == U { // expected-note {{candidate requires that the types 'T' and 'U' be equivalent (requirement specified as 'T' == 'U')}}
1212
init(_ value: U) {
1313
self.value = value
1414
}

0 commit comments

Comments
 (0)