Skip to content

Commit 46bc3b6

Browse files
authored
Merge pull request #37115 from xedin/disable-only-in-perf
[CSSimplify] Allow overload choices with missing labels to be considered for diagnostics
2 parents fe9a290 + f36ecf2 commit 46bc3b6

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
@@ -5301,7 +5301,20 @@ class DisjunctionChoice {
53015301

53025302
bool attempt(ConstraintSystem &cs) const;
53035303

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

53065319
bool hasFix() const {
53075320
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,
@@ -9726,11 +9752,57 @@ bool ConstraintSystem::simplifyAppliedOverloadsImpl(
97269752
argsWithLabels.append(args.begin(), args.end());
97279753
FunctionType::relabelParams(argsWithLabels, argumentInfo->Labels);
97289754

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

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)