Skip to content

Commit f9e08bc

Browse files
authored
Merge pull request #76174 from xedin/remodel-constraint-generation-for-assignment
[ConstraintSystem] Introduce `LValueObject` constraint to replace direct l-value assignments
2 parents 601a5c3 + dab01bc commit f9e08bc

File tree

9 files changed

+143
-42
lines changed

9 files changed

+143
-42
lines changed

include/swift/Sema/Constraint.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ enum class ConstraintKind : char {
221221
/// The first type is a tuple containing a single unlabeled element that is a
222222
/// pack expansion. The second type is its pattern type.
223223
MaterializePackExpansion,
224+
/// The first type is a l-value type whose object type is the second type.
225+
LValueObject,
224226
};
225227

226228
/// Classification of the different kinds of constraints.
@@ -691,6 +693,7 @@ class Constraint final : public llvm::ilist_node<Constraint>,
691693
case ConstraintKind::PackElementOf:
692694
case ConstraintKind::SameShape:
693695
case ConstraintKind::MaterializePackExpansion:
696+
case ConstraintKind::LValueObject:
694697
return ConstraintClassification::Relational;
695698

696699
case ConstraintKind::ValueMember:

include/swift/Sema/ConstraintSystem.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5239,6 +5239,12 @@ class ConstraintSystem {
52395239
TypeMatchOptions flags,
52405240
ConstraintLocatorBuilder locator);
52415241

5242+
/// Extract the object type of the l-value type (type1) and bind it to
5243+
/// to type2.
5244+
SolutionKind simplifyLValueObjectConstraint(Type type1, Type type2,
5245+
TypeMatchOptions flags,
5246+
ConstraintLocatorBuilder locator);
5247+
52425248
public: // FIXME: Public for use by static functions.
52435249
/// Simplify a conversion constraint with a fix applied to it.
52445250
SolutionKind simplifyFixConstraint(ConstraintFix *fix, Type type1, Type type2,

lib/Sema/CSBindings.cpp

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,30 +1318,7 @@ bool BindingSet::favoredOverDisjunction(Constraint *disjunction) const {
13181318
// Such type variables might be connected to closure as well
13191319
// e.g. when result type is optional, so it makes sense to
13201320
// open closure before attempting such disjunction.
1321-
if (auto *typeVar = boundType->lookThroughAllOptionalTypes()
1322-
->getAs<TypeVariableType>()) {
1323-
auto fixedType = CS.getFixedType(typeVar);
1324-
// Handles "assignment to an overloaded member" pattern where
1325-
// type variable that represents the destination is bound to an
1326-
// l-value type during constraint generation. See \c genAssignDestType.
1327-
//
1328-
// Note that in all other circumstances we won't be here if the
1329-
// type variable that presents the closure is connected to a
1330-
// disjunction because that would mark closure as "delayed".
1331-
if (fixedType && fixedType->is<LValueType>()) {
1332-
auto lvalueObjTy = fixedType->castTo<LValueType>()->getObjectType();
1333-
// Prefer closure only if it's not connected to the type variable
1334-
// that represents l-value object type of the assignment destination.
1335-
// Eagerly attempting closure first could result in missing some of
1336-
// the contextual annotations i.e. `@Sendable`.
1337-
if (auto *lvalueObjVar = lvalueObjTy->getAs<TypeVariableType>())
1338-
return !AdjacentVars.count(lvalueObjVar);
1339-
}
1340-
1341-
return true;
1342-
}
1343-
1344-
return false;
1321+
return boundType->lookThroughAllOptionalTypes()->is<TypeVariableType>();
13451322
}
13461323

13471324
// If this is a collection literal type, it's preferrable to bind it
@@ -1577,6 +1554,18 @@ PotentialBindings::inferFromRelational(Constraint *constraint) {
15771554
}
15781555
}
15791556

1557+
// Situations like `v.<member> = { ... }` where member is overloaded.
1558+
// We need to wait until member is resolved otherwise there is a risk
1559+
// of losing some of the contextual attributes important for the closure
1560+
// such as @Sendable and global actor.
1561+
if (TypeVar->getImpl().isClosureType() &&
1562+
kind == AllowedBindingKind::Subtypes) {
1563+
if (type->isTypeVariableOrMember() &&
1564+
constraint->getLocator()->directlyAt<AssignExpr>()) {
1565+
DelayedBy.push_back(constraint);
1566+
}
1567+
}
1568+
15801569
if (auto *locator = TypeVar->getImpl().getLocator()) {
15811570
// Don't allow a protocol type to get propagated from the base to the result
15821571
// type of a chain, Result should always be a concrete type which conforms
@@ -1586,6 +1575,24 @@ PotentialBindings::inferFromRelational(Constraint *constraint) {
15861575
return std::nullopt;
15871576
}
15881577

1578+
if (constraint->getKind() == ConstraintKind::LValueObject) {
1579+
// Allow l-value type inference from its object type, but
1580+
// not the other way around, that would be handled by constraint
1581+
// simplification.
1582+
if (kind == AllowedBindingKind::Subtypes) {
1583+
if (type->isTypeVariableOrMember())
1584+
return std::nullopt;
1585+
1586+
type = LValueType::get(type);
1587+
} else {
1588+
// Right-hand side of the l-value object constraint can only
1589+
// be bound via constraint simplification when l-value type
1590+
// is inferred or contextually from other constraints.
1591+
DelayedBy.push_back(constraint);
1592+
return std::nullopt;
1593+
}
1594+
}
1595+
15891596
// If the source of the binding is 'OptionalObject' constraint
15901597
// and type variable is on the left-hand side, that means
15911598
// that it _has_ to be of optional type, since the right-hand
@@ -1757,7 +1764,8 @@ void PotentialBindings::infer(Constraint *constraint) {
17571764
case ConstraintKind::ArgumentConversion:
17581765
case ConstraintKind::OperatorArgumentConversion:
17591766
case ConstraintKind::OptionalObject:
1760-
case ConstraintKind::UnresolvedMemberChainBase: {
1767+
case ConstraintKind::UnresolvedMemberChainBase:
1768+
case ConstraintKind::LValueObject: {
17611769
auto binding = inferFromRelational(constraint);
17621770
if (!binding)
17631771
break;

lib/Sema/CSGen.cpp

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3510,23 +3510,10 @@ namespace {
35103510
} else {
35113511
auto *locator = CS.getConstraintLocator(expr);
35123512

3513-
auto isOrCanBeLValueType = [](Type type) {
3514-
if (auto *typeVar = type->getAs<TypeVariableType>()) {
3515-
return typeVar->getImpl().canBindToLValue();
3516-
}
3517-
return type->is<LValueType>();
3518-
};
3519-
35203513
auto exprType = CS.getType(expr);
3521-
if (!isOrCanBeLValueType(exprType)) {
3522-
// Pretend that destination is an l-value type.
3523-
exprType = LValueType::get(exprType);
3524-
(void)CS.recordFix(TreatRValueAsLValue::create(CS, locator));
3525-
}
3526-
35273514
auto *destTy = CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
3528-
CS.addConstraint(ConstraintKind::Bind, LValueType::get(destTy),
3529-
exprType, locator);
3515+
CS.addConstraint(ConstraintKind::LValueObject, exprType, destTy,
3516+
locator);
35303517
return destTy;
35313518
}
35323519
}

lib/Sema/CSSimplify.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,6 +2319,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
23192319
case ConstraintKind::ExplicitGenericArguments:
23202320
case ConstraintKind::SameShape:
23212321
case ConstraintKind::MaterializePackExpansion:
2322+
case ConstraintKind::LValueObject:
23222323
llvm_unreachable("Bad constraint kind in matchTupleTypes()");
23232324
}
23242325

@@ -2679,6 +2680,7 @@ static bool matchFunctionRepresentations(FunctionType::ExtInfo einfo1,
26792680
case ConstraintKind::ExplicitGenericArguments:
26802681
case ConstraintKind::SameShape:
26812682
case ConstraintKind::MaterializePackExpansion:
2683+
case ConstraintKind::LValueObject:
26822684
return true;
26832685
}
26842686

@@ -3324,6 +3326,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
33243326
case ConstraintKind::ExplicitGenericArguments:
33253327
case ConstraintKind::SameShape:
33263328
case ConstraintKind::MaterializePackExpansion:
3329+
case ConstraintKind::LValueObject:
33273330
llvm_unreachable("Not a relational constraint");
33283331
}
33293332

@@ -7185,6 +7188,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
71857188
case ConstraintKind::ExplicitGenericArguments:
71867189
case ConstraintKind::SameShape:
71877190
case ConstraintKind::MaterializePackExpansion:
7191+
case ConstraintKind::LValueObject:
71887192
llvm_unreachable("Not a relational constraint");
71897193
}
71907194
}
@@ -14158,6 +14162,80 @@ ConstraintSystem::simplifyExplicitGenericArgumentsConstraint(
1415814162
return SolutionKind::Solved;
1415914163
}
1416014164

14165+
ConstraintSystem::SolutionKind
14166+
ConstraintSystem::simplifyLValueObjectConstraint(
14167+
Type type1, Type type2, TypeMatchOptions flags,
14168+
ConstraintLocatorBuilder locator) {
14169+
auto lvalueTy = simplifyType(type1);
14170+
14171+
auto formUnsolved = [&]() {
14172+
// If we're supposed to generate constraints, do so.
14173+
if (flags.contains(TMF_GenerateConstraints)) {
14174+
auto *lvalueObject =
14175+
Constraint::create(*this, ConstraintKind::LValueObject,
14176+
type1, type2, getConstraintLocator(locator));
14177+
14178+
addUnsolvedConstraint(lvalueObject);
14179+
return SolutionKind::Solved;
14180+
}
14181+
14182+
return SolutionKind::Unsolved;
14183+
};
14184+
14185+
auto isOrCanBeLValueType = [](Type type) {
14186+
if (auto *typeVar = type->getAs<TypeVariableType>()) {
14187+
return typeVar->getImpl().canBindToLValue();
14188+
}
14189+
return type->is<LValueType>();
14190+
};
14191+
14192+
if (lvalueTy->isPlaceholder()) {
14193+
if (!shouldAttemptFixes())
14194+
return SolutionKind::Error;
14195+
14196+
recordAnyTypeVarAsPotentialHole(type2);
14197+
return SolutionKind::Solved;
14198+
}
14199+
14200+
if (!isOrCanBeLValueType(lvalueTy)) {
14201+
if (!shouldAttemptFixes())
14202+
return SolutionKind::Error;
14203+
14204+
auto assessImpact = [&]() -> unsigned {
14205+
// If this is a projection of a member reference
14206+
// let's check whether the member is unconditionally
14207+
// settable, if so than it's a problem with its base.
14208+
if (locator.directlyAt<UnresolvedDotExpr>()) {
14209+
auto *memberLoc = getConstraintLocator(locator.getAnchor(),
14210+
ConstraintLocator::Member);
14211+
if (auto selected = findSelectedOverloadFor(memberLoc)) {
14212+
if (auto *storage = dyn_cast_or_null<AbstractStorageDecl>(
14213+
selected->choice.getDeclOrNull())) {
14214+
return storage->isSettable(nullptr) ? 1 : 2;
14215+
}
14216+
}
14217+
}
14218+
return 2;
14219+
};
14220+
14221+
if (recordFix(
14222+
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)),
14223+
assessImpact()))
14224+
return SolutionKind::Error;
14225+
14226+
lvalueTy = LValueType::get(lvalueTy);
14227+
}
14228+
14229+
if (lvalueTy->isTypeVariableOrMember())
14230+
return formUnsolved();
14231+
14232+
// TODO: This operation deserves its own locator just like OptionalObject.
14233+
addConstraint(ConstraintKind::Equal,
14234+
lvalueTy->castTo<LValueType>()->getObjectType(), type2,
14235+
getConstraintLocator(locator));
14236+
return SolutionKind::Solved;
14237+
}
14238+
1416114239
static llvm::PointerIntPair<Type, 3, unsigned>
1416214240
getBaseTypeForPointer(TypeBase *type) {
1416314241
unsigned unwrapCount = 0;
@@ -15625,6 +15703,9 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,
1562515703
return simplifyMaterializePackExpansionConstraint(first, second, subflags,
1562615704
locator);
1562715705

15706+
case ConstraintKind::LValueObject:
15707+
return simplifyLValueObjectConstraint(first, second, subflags, locator);
15708+
1562815709
case ConstraintKind::ValueMember:
1562915710
case ConstraintKind::UnresolvedValueMember:
1563015711
case ConstraintKind::ValueWitness:
@@ -16220,6 +16301,11 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
1622016301
return simplifyMaterializePackExpansionConstraint(
1622116302
constraint.getFirstType(), constraint.getSecondType(),
1622216303
/*flags*/ std::nullopt, constraint.getLocator());
16304+
16305+
case ConstraintKind::LValueObject:
16306+
return simplifyLValueObjectConstraint(
16307+
constraint.getFirstType(), constraint.getSecondType(),
16308+
/*flags*/ std::nullopt, constraint.getLocator());
1622316309
}
1622416310

1622516311
llvm_unreachable("Unhandled ConstraintKind in switch.");

lib/Sema/Constraint.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second,
8585
case ConstraintKind::ExplicitGenericArguments:
8686
case ConstraintKind::SameShape:
8787
case ConstraintKind::MaterializePackExpansion:
88+
case ConstraintKind::LValueObject:
8889
assert(!First.isNull());
8990
assert(!Second.isNull());
9091
break;
@@ -176,6 +177,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third,
176177
case ConstraintKind::ExplicitGenericArguments:
177178
case ConstraintKind::SameShape:
178179
case ConstraintKind::MaterializePackExpansion:
180+
case ConstraintKind::LValueObject:
179181
llvm_unreachable("Wrong constructor");
180182

181183
case ConstraintKind::KeyPath:
@@ -326,6 +328,7 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const {
326328
case ConstraintKind::ExplicitGenericArguments:
327329
case ConstraintKind::SameShape:
328330
case ConstraintKind::MaterializePackExpansion:
331+
case ConstraintKind::LValueObject:
329332
return create(cs, getKind(), getFirstType(), getSecondType(), getLocator());
330333

331334
case ConstraintKind::ApplicableFunction:
@@ -590,6 +593,10 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm,
590593
Out << " materialize pack expansion ";
591594
break;
592595

596+
case ConstraintKind::LValueObject:
597+
Out << " l-value object type ";
598+
break;
599+
593600
case ConstraintKind::Disjunction:
594601
llvm_unreachable("disjunction handled above");
595602
case ConstraintKind::Conjunction:
@@ -760,6 +767,7 @@ gatherReferencedTypeVars(Constraint *constraint,
760767
case ConstraintKind::ExplicitGenericArguments:
761768
case ConstraintKind::SameShape:
762769
case ConstraintKind::MaterializePackExpansion:
770+
case ConstraintKind::LValueObject:
763771
constraint->getFirstType()->getTypeVariables(typeVars);
764772
constraint->getSecondType()->getTypeVariables(typeVars);
765773
break;

test/Constraints/diagnostics.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,7 @@ func r23560128() {
647647
var a : (Int,Int)?
648648
a.0 = 42 // expected-error{{value of optional type '(Int, Int)?' must be unwrapped to refer to member '0' of wrapped base type '(Int, Int)'}}
649649
// expected-note@-1{{chain the optional }}
650+
// expected-note@-2 {{force-unwrap using '!' }}
650651
}
651652

652653
// <rdar://problem/21890157> QoI: wrong error message when accessing properties on optional structs without unwrapping
@@ -656,6 +657,7 @@ struct ExampleStruct21890157 {
656657
var example21890157: ExampleStruct21890157?
657658
example21890157.property = "confusing" // expected-error {{value of optional type 'ExampleStruct21890157?' must be unwrapped to refer to member 'property' of wrapped base type 'ExampleStruct21890157'}}
658659
// expected-note@-1{{chain the optional }}
660+
// expected-note@-2 {{force-unwrap using '!' }}
659661

660662

661663
struct UnaryOp {}

test/ImportResolution/import-resolution-overload.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ scopedFunction = 42
4646
// FIXME: Should be an error -- a type name and a function cannot overload.
4747
var _ : Int = TypeNameWins(42)
4848

49-
TypeNameWins = 42 // expected-error {{cannot assign to immutable expression of type 'Int'}}
49+
// FIXME: This should be an ambiguity where both candidates are mentioned as notes.
50+
TypeNameWins = 42 // expected-error {{cannot assign to immutable expression of type '(Int) -> Int'}}
5051
var _ : TypeNameWins // no-warning
5152

5253
// rdar://problem/21739333

validation-test/Sema/SwiftUI/rdar84580119.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extension EnvironmentValues {
1414
var myHorizontalAlignment: AlignmentID? {
1515
get { fatalError() }
1616
set { self[\.MyHorizontalAlignmentEnvironmentKey.self] = newValue }
17-
// expected-error@-1 {{generic parameter 'K' could not be inferred}}
17+
// expected-error@-1 {{subscript 'subscript(_:)' requires that 'any AlignmentID' be a class type}}
1818
// expected-error@-2 {{cannot infer key path type from context; consider explicitly specifying a root type}}
1919
}
2020
}

0 commit comments

Comments
 (0)