Skip to content

Commit c49aeaf

Browse files
authored
Merge pull request swiftlang#76354 from xedin/improve-mismatch-diagnostics-in-optional-context
[CSSimplify] Rework how/when mismatches between optional types are fixed
2 parents 7e71641 + 8fca2c5 commit c49aeaf

31 files changed

+152
-178
lines changed

include/swift/Sema/CSFix.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,10 @@ class TreatRValueAsLValue final : public ConstraintFix {
620620
return diagnose(*commonFixes.front().first);
621621
}
622622

623+
/// Assess the impact this fix is going to have at the given location.
624+
static unsigned assessImpact(ConstraintSystem &cs,
625+
ConstraintLocator *atLoc);
626+
623627
static TreatRValueAsLValue *create(ConstraintSystem &cs,
624628
ConstraintLocator *locator);
625629

lib/Sema/CSDiagnostics.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,17 @@ Type RequirementFailure::getOwnerType() const {
242242
// to convert source to destination, which means that
243243
// owner type is actually not an assignment expression
244244
// itself but its source.
245-
if (auto *assignment = getAsExpr<AssignExpr>(anchor))
245+
if (auto *assignment = getAsExpr<AssignExpr>(anchor)) {
246246
anchor = assignment->getSrc();
247+
// If locator points to a tuple element, let's dig that up.
248+
// Situations like `<<dest>> = (v, 2)` where `v` has a requirement failure.
249+
if (auto tupleEltIdx =
250+
getLocator()->findFirst<LocatorPathElt::AnyTupleElement>()) {
251+
if (auto *tuple = getAsExpr<TupleExpr>(anchor)) {
252+
return getType(tuple->getElement(tupleEltIdx->getIndex()));
253+
}
254+
}
255+
}
247256

248257
return getType(anchor)->getInOutObjectType()->getMetatypeInstanceType();
249258
}

lib/Sema/CSFix.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,29 @@ bool TreatRValueAsLValue::diagnose(const Solution &solution,
141141
return failure.diagnose(asNote);
142142
}
143143

144+
unsigned TreatRValueAsLValue::assessImpact(ConstraintSystem &cs,
145+
ConstraintLocator *atLoc) {
146+
// Results of calls can never be l-value.
147+
unsigned impact = isExpr<CallExpr>(atLoc->getAnchor()) ? 2 : 1;
148+
// An overload choice that isn't settable is least interesting for
149+
// diagnosis.
150+
auto *calleeLoc = cs.getCalleeLocator(atLoc, /*lookThroughApply=*/false);
151+
if (auto overload = cs.findSelectedOverloadFor(calleeLoc)) {
152+
if (auto *var = dyn_cast_or_null<AbstractStorageDecl>(
153+
overload->choice.getDeclOrNull())) {
154+
impact += !var->isSettableInSwift(cs.DC) ? 1 : 0;
155+
} else {
156+
impact += 1;
157+
}
158+
}
159+
160+
// This is extra impactful if location has other issues.
161+
if (cs.hasFixFor(atLoc) || cs.hasFixFor(calleeLoc))
162+
impact += 2;
163+
164+
return impact;
165+
}
166+
144167
TreatRValueAsLValue *TreatRValueAsLValue::create(ConstraintSystem &cs,
145168
ConstraintLocator *locator) {
146169
if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>())

lib/Sema/CSSimplify.cpp

Lines changed: 36 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -3978,8 +3978,19 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
39783978
path.back().is<LocatorPathElt::GenericArgument>()))
39793979
path.pop_back();
39803980

3981+
auto *fixLoc = getConstraintLocator(anchor, path);
3982+
// If after looking through optionals and generic arguments
3983+
// we end up directly on assignment this is a source/destination
3984+
// type mismatch.
3985+
if (fixLoc->directlyAt<AssignExpr>()) {
3986+
auto *fix = IgnoreAssignmentDestinationType::create(
3987+
*this, type1, type2, fixLoc);
3988+
return recordFix(fix) ? getTypeMatchFailure(locator)
3989+
: getTypeMatchSuccess();
3990+
}
3991+
39813992
auto *fix = AllowNonClassTypeToConvertToAnyObject::create(
3982-
*this, type1, getConstraintLocator(anchor, path));
3993+
*this, type1, fixLoc);
39833994

39843995
return recordFix(fix) ? getTypeMatchFailure(locator)
39853996
: getTypeMatchSuccess();
@@ -5341,6 +5352,9 @@ bool ConstraintSystem::repairFailures(
53415352
if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
53425353
return true;
53435354

5355+
if (hasAnyRestriction())
5356+
return false;
5357+
53445358
// If destination is `AnyObject` it means that source doesn't conform.
53455359
if (rhs->getWithoutSpecifierType()
53465360
->lookThroughAllOptionalTypes()
@@ -5350,60 +5364,6 @@ bool ConstraintSystem::repairFailures(
53505364
return true;
53515365
}
53525366

5353-
// If we are trying to assign e.g. `Array<Int>` to `Array<Float>` let's
5354-
// give solver a chance to determine which generic parameters are
5355-
// mismatched and produce a fix for that.
5356-
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
5357-
return false;
5358-
5359-
// An attempt to assign `Int?` to `String?`.
5360-
if (hasConversionOrRestriction(
5361-
ConversionRestrictionKind::OptionalToOptional)) {
5362-
conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create(
5363-
*this, lhs, rhs, getConstraintLocator(locator)));
5364-
return true;
5365-
}
5366-
5367-
// If the situation has to do with protocol composition types and
5368-
// destination doesn't have one of the conformances e.g. source is
5369-
// `X & Y` but destination is only `Y` or vice versa, there is a
5370-
// tailored "missing conformance" fix for that.
5371-
if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
5372-
return false;
5373-
5374-
if (hasConversionOrRestriction(
5375-
ConversionRestrictionKind::MetatypeToExistentialMetatype) ||
5376-
hasConversionOrRestriction(
5377-
ConversionRestrictionKind::ExistentialMetatypeToMetatype) ||
5378-
hasConversionOrRestriction(ConversionRestrictionKind::Superclass)) {
5379-
conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create(
5380-
*this, lhs, rhs, getConstraintLocator(locator)));
5381-
return true;
5382-
}
5383-
5384-
if (hasConversionOrRestriction(
5385-
ConversionRestrictionKind::ValueToOptional)) {
5386-
lhs = lhs->lookThroughAllOptionalTypes();
5387-
rhs = rhs->lookThroughAllOptionalTypes();
5388-
5389-
// If both object types are functions, let's allow the solver to
5390-
// structurally compare them before trying to fix anything.
5391-
if (lhs->is<FunctionType>() && rhs->is<FunctionType>())
5392-
return false;
5393-
5394-
// If either object type is a generic, nominal or existential type
5395-
// it means that follow-up to value-to-optional is going to be:
5396-
//
5397-
// 1. "deep equality" check, which is handled by generic argument(s)
5398-
// or contextual mismatch fix, or
5399-
// 2. "existential" check, which is handled by a missing conformance
5400-
// fix.
5401-
if ((lhs->is<BoundGenericType>() && rhs->is<BoundGenericType>()) ||
5402-
(lhs->is<NominalType>() && rhs->is<NominalType>()) ||
5403-
rhs->isAnyExistentialType())
5404-
return false;
5405-
}
5406-
54075367
auto *destExpr = AE->getDest();
54085368
// Literal expression as well as call/operator application can't be
54095369
// used as an assignment destination because resulting type is immutable.
@@ -14062,26 +14022,9 @@ ConstraintSystem::simplifyLValueObjectConstraint(
1406214022
if (!shouldAttemptFixes())
1406314023
return SolutionKind::Error;
1406414024

14065-
auto assessImpact = [&]() -> unsigned {
14066-
// If this is a projection of a member reference
14067-
// let's check whether the member is unconditionally
14068-
// settable, if so than it's a problem with its base.
14069-
if (locator.directlyAt<UnresolvedDotExpr>()) {
14070-
auto *memberLoc = getConstraintLocator(locator.getAnchor(),
14071-
ConstraintLocator::Member);
14072-
if (auto selected = findSelectedOverloadFor(memberLoc)) {
14073-
if (auto *storage = dyn_cast_or_null<AbstractStorageDecl>(
14074-
selected->choice.getDeclOrNull())) {
14075-
return storage->isSettable(nullptr) ? 1 : 2;
14076-
}
14077-
}
14078-
}
14079-
return 2;
14080-
};
14081-
14082-
if (recordFix(
14083-
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)),
14084-
assessImpact()))
14025+
auto *fixLoc = getConstraintLocator(locator);
14026+
if (recordFix(TreatRValueAsLValue::create(*this, fixLoc),
14027+
TreatRValueAsLValue::assessImpact(*this, fixLoc)))
1408514028
return SolutionKind::Error;
1408614029

1408714030
lvalueTy = LValueType::get(lvalueTy);
@@ -14215,14 +14158,23 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
1421514158
if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch))
1421614159
return true;
1421714160

14218-
if (restriction == ConversionRestrictionKind::ValueToOptional ||
14219-
restriction == ConversionRestrictionKind::OptionalToOptional)
14220-
++impact;
14161+
if (restriction == ConversionRestrictionKind::ValueToOptional) {
14162+
// If this is an optional injection we can drop optional from
14163+
// "to" type since it's not significant for the diagnostic.
14164+
toType = toType->getOptionalObjectType();
14165+
}
1422114166

14222-
auto *fix =
14223-
loc->isLastElement<LocatorPathElt::ApplyArgToParam>()
14224-
? AllowArgumentMismatch::create(*this, fromType, toType, loc)
14225-
: ContextualMismatch::create(*this, fromType, toType, loc);
14167+
ConstraintFix *fix = nullptr;
14168+
if (loc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
14169+
fix = AllowArgumentMismatch::create(*this, fromType, toType, loc);
14170+
} else if (loc->isForAssignment()) {
14171+
fix = IgnoreAssignmentDestinationType::create(*this, fromType, toType,
14172+
loc);
14173+
} else {
14174+
fix = ContextualMismatch::create(*this, fromType, toType, loc);
14175+
}
14176+
14177+
assert(fix);
1422614178
return !recordFix(fix, impact);
1422714179
}
1422814180

@@ -15228,21 +15180,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1522815180
}
1522915181

1523015182
case FixKind::TreatRValueAsLValue: {
15231-
unsigned impact = 1;
15232-
// If this is an attempt to use result of a function/subscript call as
15233-
// an l-value, it has to have an increased impact because it's either
15234-
// a function - which is completely incorrect, or it's a get-only
15235-
// subscript, which requires changes to declaration to become mutable.
15236-
impact += (locator.endsWith<LocatorPathElt::FunctionResult>() ||
15237-
locator.endsWith<LocatorPathElt::SubscriptMember>())
15238-
? 1
15239-
: 0;
15240-
// An overload choice that isn't settable is least interesting for diagnosis.
15241-
if (auto overload = findSelectedOverloadFor(getCalleeLocator(fix->getLocator()))) {
15242-
if (auto *var = dyn_cast_or_null<VarDecl>(overload->choice.getDeclOrNull())) {
15243-
impact += !var->isSettableInSwift(DC) ? 1 : 0;
15244-
}
15245-
}
15183+
unsigned impact =
15184+
TreatRValueAsLValue::assessImpact(*this, fix->getLocator());
1524615185
return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
1524715186
}
1524815187

test/APINotes/type_changes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ import APINotesFrameworkTest
77

88

99
func testChangedTypes(tc: TypeChanges, a: A, i: Int) {
10-
_ = tc.method(with: i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'A?'}}
10+
_ = tc.method(with: i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'A'}}
1111
let _: Int = tc.method(with: a) // expected-error{{cannot convert value of type 'A' to specified type 'Int'}}
1212
}

test/ClangImporter/MixedSource/import-mixed-framework-with-forward.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ BridgingHeader.takeForward(SwiftClass(x: 42))
1616
BridgingHeader.takeRenamedForward(CustomNameClass())
1717

1818
// Check that we're compiling at all.
19-
BridgingHeader.takeRenamedForward(SwiftClass(x: 42)) // expected-error {{cannot convert value of type 'SwiftClass' to expected argument type 'CustomNameClass?'}}
19+
BridgingHeader.takeRenamedForward(SwiftClass(x: 42)) // expected-error {{cannot convert value of type 'SwiftClass' to expected argument type 'CustomNameClass'}}

test/ClangImporter/cf.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func test0(_ fridge: CCRefrigerator) {
1616

1717
func test1(_ power: Unmanaged<CCPowerSupply>) {
1818
assertUnmanaged(power)
19-
let fridge = CCRefrigeratorCreate(power) // expected-error {{cannot convert value of type 'Unmanaged<CCPowerSupply>' to expected argument type 'CCPowerSupply?'}}
19+
let fridge = CCRefrigeratorCreate(power) // expected-error {{cannot convert value of type 'Unmanaged<CCPowerSupply>' to expected argument type 'CCPowerSupply'}}
2020
assertUnmanaged(fridge) // expected-error {{generic parameter 'T' could not be inferred}}
2121
}
2222

@@ -64,9 +64,9 @@ func test9() {
6464
CCRefrigeratorOpen(fridge)
6565
let item = CCRefrigeratorGet(fridge, 0).takeUnretainedValue()
6666
// TODO(diagnostics): In this case we should probably suggest to flip `item` and `fridge`
67-
CCRefrigeratorInsert(item, fridge) // expected-error {{cannot convert value of type 'CCItem' to expected argument type 'CCMutableRefrigerator?'}}
68-
// expected-error@-1 {{cannot convert value of type 'CCMutableRefrigerator' to expected argument type 'CCItem?'}}
69-
CCRefrigeratorInsert(constFridge, item) // expected-error {{cannot convert value of type 'CCRefrigerator' to expected argument type 'CCMutableRefrigerator?'}}
67+
CCRefrigeratorInsert(item, fridge) // expected-error {{cannot convert value of type 'CCItem' to expected argument type 'CCMutableRefrigerator'}}
68+
// expected-error@-1 {{cannot convert value of type 'CCMutableRefrigerator' to expected argument type 'CCItem'}}
69+
CCRefrigeratorInsert(constFridge, item) // expected-error {{cannot convert value of type 'CCRefrigerator' to expected argument type 'CCMutableRefrigerator'}}
7070
CCRefrigeratorInsert(fridge, item)
7171
CCRefrigeratorClose(fridge)
7272
}
@@ -121,12 +121,12 @@ func testOutParametersBad() {
121121

122122
let power: CCPowerSupply?
123123
CCRefrigeratorGetPowerSupplyIndirect(0, power)
124-
// expected-error@-1:40 {{cannot convert value of type 'Int' to expected argument type 'CCRefrigerator?'}}
124+
// expected-error@-1:40 {{cannot convert value of type 'Int' to expected argument type 'CCRefrigerator'}}
125125
// expected-error@-2:43 {{cannot convert value of type 'CCPowerSupply?' to expected argument type 'AutoreleasingUnsafeMutablePointer<CCPowerSupply?>'}}
126126

127127
let item: CCItem?
128128
CCRefrigeratorGetItemUnaudited(0, 0, item)
129-
// expected-error@-1:34 {{cannot convert value of type 'Int' to expected argument type 'CCRefrigerator?'}}
129+
// expected-error@-1:34 {{cannot convert value of type 'Int' to expected argument type 'CCRefrigerator'}}
130130
// expected-error@-2:40 {{cannot convert value of type 'CCItem?' to expected argument type 'UnsafeMutablePointer<Unmanaged<CCItem>?>?'}}
131131
}
132132

test/ClangImporter/cfuncs_parse.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ func test_nested_pointers() {
187187
nested_pointer_audited(nil)
188188
nested_pointer_audited2(nil) // expected-error {{'nil' is not compatible with expected argument type 'UnsafePointer<UnsafePointer<Int32>?>'}}
189189

190-
nested_pointer(0) // expected-error {{expected argument type 'UnsafePointer<UnsafePointer<Int32>?>?'}}
191-
nested_pointer_audited(0) // expected-error {{expected argument type 'UnsafePointer<UnsafePointer<Int32>>?'}}
190+
nested_pointer(0) // expected-error {{expected argument type 'UnsafePointer<UnsafePointer<Int32>?>'}}
191+
nested_pointer_audited(0) // expected-error {{expected argument type 'UnsafePointer<UnsafePointer<Int32>>'}}
192192
nested_pointer_audited2(0) // expected-error {{expected argument type 'UnsafePointer<UnsafePointer<Int32>?>'}}
193193
}
194194

test/ClangImporter/nullability.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,5 @@ func testCF(_ fridge: CCRefrigerator) {
6666
CCRefrigeratorOpenMaybeDoSomething(fridge) // okay
6767
CCRefrigeratorOpenMaybeDoSomething(nil) // okay
6868

69-
CCRefrigeratorOpenMaybeDoSomething(5) // expected-error{{cannot convert value of type 'Int' to expected argument type 'CCRefrigerator?'}}
69+
CCRefrigeratorOpenMaybeDoSomething(5) // expected-error{{cannot convert value of type 'Int' to expected argument type 'CCRefrigerator'}}
7070
}

test/ClangImporter/objc_id_as_any.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ idLover.takesArray(ofId: &y) // expected-error{{cannot convert value of type 'Un
2727
idLover.takesId(x)
2828
idLover.takesId(y)
2929

30-
install_global_event_handler(idLover) // expected-error {{cannot convert value of type 'NSIdLover' to expected argument type 'event_handler?' (aka 'Optional<@convention(c) (Any) -> ()>')}}
30+
install_global_event_handler(idLover) // expected-error {{cannot convert value of type 'NSIdLover' to expected argument type 'event_handler' (aka '@convention(c) (Any) -> ()')}}
3131

3232
// FIXME: this should not type-check!
3333
// Function conversions are not legal when converting to a thin function type.

0 commit comments

Comments
 (0)