Skip to content

Commit 5dfb234

Browse files
committed
[Sema] prevent inverse-constraints on outer params
Since there is no propagation of inverse constraints in the requirement machine, we need to fully desugar these requirements at the point of defining a generic parameter. That desugaring involves determining which default conformance requirements need to be applied to a generic parameter, accounting for inverses. But, nested generic contexts in scope of those expanded generic parameters can still write constraints on that outer parameter. For example, this method's where clause can have its own constraints on `T`: ``` struct S<T> { func f() where T: ~Copyable {} } ``` But, the generic signature of `S` already has a `T: Copyable` that was expanded. The method `f` will always see a `T` that conforms to `Copyable`, so it's impossible for `f` to claim that it applies for `T`'s that lack Copyable. Put another way, it's not valid for this method `f`, whose generic signature is based on its parent's `S`, to weaken or remove requirements from parent's signature. Only positive requirements can be added to them.
1 parent 30b09c9 commit 5dfb234

File tree

6 files changed

+101
-8
lines changed

6 files changed

+101
-8
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3088,6 +3088,11 @@ ERROR(requires_not_suitable_inverse_subject,none,
30883088
"cannot apply inverse %1 to type %0 in conformance requirement",
30893089
(Type, Type))
30903090

3091+
ERROR(requires_not_suitable_inverse_outer_subject,none,
3092+
"cannot add inverse constraint '%0: %1' "
3093+
"on generic parameter '%0' defined in outer scope",
3094+
(StringRef, StringRef))
3095+
30913096
ERROR(invalid_shape_requirement,none,
30923097
"invalid same-shape requirement between %0 and %1",
30933098
(Type, Type))

lib/AST/RequirementMachine/Diagnostics.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,23 @@ bool swift::rewriting::diagnoseRequirementErrors(
122122
break;
123123
}
124124

125+
case RequirementError::Kind::InvalidInverseOuterSubject: {
126+
if (error.requirement.hasError())
127+
break;
128+
129+
assert(error.requirement.getKind() == RequirementKind::Conformance);
130+
131+
auto subjectType = error.requirement.getFirstType();
132+
auto constraintType = error.requirement.getSecondType();
133+
134+
assert(constraintType->is<InverseType>());
135+
136+
ctx.Diags.diagnose(loc, diag::requires_not_suitable_inverse_outer_subject,
137+
subjectType.getString(), constraintType.getString());
138+
diagnosedError = true;
139+
break;
140+
}
141+
125142
case RequirementError::Kind::InvalidShapeRequirement: {
126143
if (error.requirement.hasError())
127144
break;

lib/AST/RequirementMachine/Diagnostics.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ struct RequirementError {
3737
/// An inverse constraint applied to an invalid subject type,
3838
/// e.g., each T : ~Copyable
3939
InvalidInverseSubject,
40+
/// The inverse constraint requirement cannot applied to the subject because
41+
/// it's an outer generic parameter, e.g.,
42+
/// protocol P { func f() where Self: ~Copyable }
43+
InvalidInverseOuterSubject,
4044
/// An invalid shape requirement, e.g. T.shape == Int.shape
4145
InvalidShapeRequirement,
4246
/// A pair of conflicting requirements, T == Int, T == String
@@ -86,6 +90,11 @@ struct RequirementError {
8690
return {Kind::InvalidInverseSubject, req, loc};
8791
}
8892

93+
static RequirementError forInvalidInverseOuterSubject(Requirement req,
94+
SourceLoc loc) {
95+
return {Kind::InvalidInverseOuterSubject, req, loc};
96+
}
97+
8998
static RequirementError forInvalidShapeRequirement(Requirement req,
9099
SourceLoc loc) {
91100
return {Kind::InvalidShapeRequirement, req, loc};

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,9 @@ void swift::rewriting::realizeRequirement(
768768
/// Desugars and expands all default conformance requirements on \c subject,
769769
/// accounting for and eliminating inverse constraints.
770770
///
771+
/// For inverse constraints on subjects other than the ones specified, they are
772+
/// removed and diagnosed as errors.
773+
///
771774
/// \param subjects the subject types for which we should expand defaults.
772775
void swift::rewriting::expandDefaultRequirements(ASTContext &ctx,
773776
SmallVectorImpl<Type> const& subjects,
@@ -791,10 +794,22 @@ void swift::rewriting::expandDefaultRequirements(ASTContext &ctx,
791794
if (!inverse)
792795
return false;
793796

797+
// At this point, we have a conformance constraint with an inverse.
794798
auto subject = req.getFirstType()->getCanonicalType();
795799

796-
if (defaults.count(subject) == 0)
797-
return false;
800+
// If the inverse is on a subject that wasn't permitted by our caller, then
801+
// remove and diagnose as an error. This can happen when an inner context
802+
// has a constraint on some outer generic parameter, e.g.,
803+
//
804+
// protocol P {
805+
// func f() where Self: ~Copyable
806+
// }
807+
//
808+
if (defaults.count(subject) == 0) {
809+
errors.push_back(
810+
RequirementError::forInvalidInverseOuterSubject(req, structReq.loc));
811+
return true;
812+
}
798813

799814
// TODO: check for redundant inverses.
800815

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -837,12 +837,15 @@ InferredGenericSignatureRequest::evaluate(
837837
auto *moduleForInference = lookupDC->getParentModule();
838838
auto &ctx = moduleForInference->getASTContext();
839839

840-
// Expand default requirements for all generic parameters.
841-
SmallVector<Type, 8> gpTypes;
842-
for (auto gtpt : genericParams)
843-
gpTypes.push_back(Type(gtpt)); // NOTE: wish we had std::views::transform
844-
845-
expandDefaultRequirements(ctx, gpTypes, requirements, errors);
840+
// After realizing requirements, expand default requirements only for local
841+
// generic parameters, as the outer parameters have already been expanded.
842+
SmallVector<Type, 4> localGPs;
843+
if (genericParamList)
844+
for (auto *gtpd : genericParamList->getParams())
845+
localGPs.push_back(gtpd->getDeclaredInterfaceType());
846+
847+
// Expand defaults and eliminate all inverse-conformance requirements.
848+
expandDefaultRequirements(ctx, localGPs, requirements, errors);
846849

847850
// Perform requirement inference from function parameter and result
848851
// types and such.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics
2+
3+
// REQUIRES: asserts
4+
5+
protocol NoCopyReq: ~Copyable {}
6+
7+
protocol P {
8+
func f() where Self: ~Copyable // expected-error {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
9+
10+
func g<T>(_: T) where Self: ~Copyable // expected-error {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
11+
12+
associatedtype AT where Self: ~Copyable // expected-error {{constraint with subject type of 'Self' is not supported; consider adding requirement to protocol inheritance clause instead}}
13+
14+
// expected-error@+1 {{cannot add inverse constraint 'Self.Alice: ~Copyable' on generic parameter 'Self.Alice' defined in outer scope}}
15+
associatedtype Bob where Alice: NoCopyReq & ~Copyable
16+
associatedtype Alice where Bob: ~Copyable
17+
// expected-error@-1 {{cannot add inverse constraint 'Self.Bob: ~Copyable' on generic parameter 'Self.Bob' defined in outer scope}}
18+
}
19+
20+
protocol U {}
21+
22+
extension U where Self: ~Copyable {} // expected-error {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
23+
24+
extension P {
25+
func g() where Self: ~Copyable {} // expected-error {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
26+
27+
typealias Me = Self where Self: ~Copyable // expected-error {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
28+
29+
typealias MeAndU = Self where Self: U
30+
}
31+
32+
struct S<T> {
33+
34+
// expected-note@+2 3{{add}}
35+
// expected-error@+1 {{parameter of noncopyable type 'U' must specify ownership}}
36+
func fn<U>(_ u: U)
37+
where T: ~Copyable, // expected-error {{cannot add inverse constraint 'T: ~Copyable' on generic parameter 'T' defined in outer scope}}
38+
U: ~Copyable
39+
{}
40+
41+
func onlyCopyable() where T: Copyable {}
42+
}
43+
44+
extension S where T: NoCopyReq & ~Copyable {} // expected-error {{cannot add inverse constraint 'T: ~Copyable' on generic parameter 'T' defined in outer scope}}

0 commit comments

Comments
 (0)