Skip to content

Commit 60da82b

Browse files
theblixguyslavapestov
authored andcommitted
[GSB] [Diag] Constraint to concrete type using ":" should offer a fix-it (#22152)
* [diag] add a diagnostic note for the fixit * [gsb] emit a diagnostic with fixit to replace ':' with '==' * [gsb] rename variable * [gsb] replace dyn_cast with isa * [test] add a test case * [test] update tests * [gsb] emit diagnostic for protocols as well * [gsb] simplify if statement * [gsb] rename a variable * [gsb] Create a helper to remove Self. prefix and add a new test case * [gsb] simplify checks * [gsb] move the diagnostic code to finalize() * [gsb] re-indent * [gsb] fix a typo * [gsb] pass values as copy * [gsb] show a fixit if the subject type is a member type * [test] update diagnostics in existing tests * [gsb] check if the subject type has an assoc type decl * [gsb] use requirement source * [test] add new tests * [gsb] use constraint struct and rename to invalidIsaConstraints
1 parent 536e706 commit 60da82b

File tree

4 files changed

+92
-3
lines changed

4 files changed

+92
-3
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,6 +1982,9 @@ ERROR(protocol_composition_one_class,none,
19821982
ERROR(requires_conformance_nonprotocol,none,
19831983
"type %0 constrained to non-protocol, non-class type %1",
19841984
(Type, Type))
1985+
NOTE(requires_conformance_nonprotocol_fixit,none,
1986+
"use '%0 == %1' to require '%0' to be '%1'",
1987+
(StringRef, StringRef))
19851988
ERROR(requires_not_suitable_archetype,none,
19861989
"type %0 in conformance requirement does not refer to a "
19871990
"generic parameter or associated type",

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ class GenericSignatureBuilder {
314314
/// \c ConstraintResult::Unresolved so the caller knows what happened.
315315
GenerateUnresolved = 1,
316316
};
317+
318+
/// The set of constraints that are invalid because the constraint
319+
/// type isn't constrained to a protocol or a class
320+
std::vector<Constraint<Type>> invalidIsaConstraints;
317321

318322
private:
319323
class InferRequirementsWalker;

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4629,8 +4629,14 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement(
46294629
->getDependentType(getGenericParams());
46304630

46314631
Impl->HadAnyError = true;
4632-
Diags.diagnose(source.getLoc(), diag::requires_conformance_nonprotocol,
4633-
subjectType, constraintType);
4632+
4633+
if (subjectType->is<DependentMemberType>()) {
4634+
subjectType = resolveDependentMemberTypes(*this, subjectType);
4635+
}
4636+
4637+
auto invalidConstraint = Constraint<Type>(
4638+
{subject, constraintType, source.getSource(*this, subjectType)});
4639+
invalidIsaConstraints.push_back(invalidConstraint);
46344640
}
46354641

46364642
return ConstraintResult::Conflicting;
@@ -5750,6 +5756,43 @@ GenericSignatureBuilder::finalize(SourceLoc loc,
57505756
}
57515757
}
57525758
}
5759+
5760+
// Emit a diagnostic if we recorded any constraints where the constraint
5761+
// type was not constrained to a protocol or class. Provide a fix-it if
5762+
// allowConcreteGenericParams is true or the subject type is a member type.
5763+
if (!invalidIsaConstraints.empty()) {
5764+
for (auto constraint : invalidIsaConstraints) {
5765+
auto subjectType = constraint.getSubjectDependentType(getGenericParams());
5766+
auto constraintType = constraint.value;
5767+
auto source = constraint.source;
5768+
auto loc = source->getLoc();
5769+
5770+
Diags.diagnose(loc, diag::requires_conformance_nonprotocol,
5771+
subjectType, constraintType);
5772+
5773+
auto getNameWithoutSelf = [&](std::string subjectTypeName) {
5774+
std::string selfSubstring = "Self.";
5775+
5776+
if (subjectTypeName.rfind(selfSubstring, 0) == 0) {
5777+
return subjectTypeName.erase(0, selfSubstring.length());
5778+
}
5779+
5780+
return subjectTypeName;
5781+
};
5782+
5783+
if (allowConcreteGenericParams ||
5784+
(subjectType->is<DependentMemberType>() &&
5785+
!source->isProtocolRequirement())) {
5786+
auto subjectTypeName = subjectType.getString();
5787+
auto subjectTypeNameWithoutSelf = getNameWithoutSelf(subjectTypeName);
5788+
Diags.diagnose(loc, diag::requires_conformance_nonprotocol_fixit,
5789+
subjectTypeNameWithoutSelf, constraintType.getString())
5790+
.fixItReplace(loc, " == ");
5791+
}
5792+
}
5793+
5794+
invalidIsaConstraints.clear();
5795+
}
57535796
}
57545797

57555798
/// Turn a requirement right-hand side into an unresolved type.

test/Constraints/generics.swift

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,4 +637,43 @@ func compare<C: Collection, Key: Hashable, Value: Equatable>(c: C)
637637
-> Bool where C.Element == (key: Key, value: Value)
638638
{
639639
_ = Dictionary(uniqueKeysWithValues: Array(c))
640-
}
640+
}
641+
642+
// https://bugs.swift.org/browse/SR-7984
643+
struct SR_7984<Bar> {
644+
func doSomething() {}
645+
}
646+
647+
extension SR_7984 where Bar: String {} // expected-error {{type 'Bar' constrained to non-protocol, non-class type 'String'}} expected-note {{use 'Bar == String' to require 'Bar' to be 'String'}} {{28-29= ==}}
648+
649+
protocol SR_7984_Proto {
650+
associatedtype Bar
651+
}
652+
653+
extension SR_7984_Proto where Bar: String {} // expected-error {{type 'Self.Bar' constrained to non-protocol, non-class type 'String'}} expected-note {{use 'Bar == String' to require 'Bar' to be 'String'}} {{34-35= ==}}
654+
655+
protocol SR_7984_HasFoo {
656+
associatedtype Foo
657+
}
658+
protocol SR_7984_HasAssoc {
659+
associatedtype Assoc: SR_7984_HasFoo
660+
}
661+
662+
struct SR_7984_X<T: SR_7984_HasAssoc> {}
663+
extension SR_7984_X where T.Assoc.Foo: String {} // expected-error {{type 'T.Assoc.Foo' constrained to non-protocol, non-class type 'String'}} expected-note {{use 'T.Assoc.Foo == String' to require 'T.Assoc.Foo' to be 'String'}} {{38-39= ==}}
664+
665+
struct SR_7984_S<T: Sequence> where T.Element: String {} // expected-error {{type 'T.Element' constrained to non-protocol, non-class type 'String'}} expected-note {{use 'T.Element == String' to require 'T.Element' to be 'String'}} {{46-47= ==}}
666+
func SR_7984_F<T: Sequence>(foo: T) where T.Element: String {} // expected-error {{type 'T.Element' constrained to non-protocol, non-class type 'String'}} expected-note {{use 'T.Element == String' to require 'T.Element' to be 'String'}} {{52-53= ==}}
667+
668+
protocol SR_7984_P {
669+
func S<T : Sequence>(bar: T) where T.Element: String // expected-error {{type 'T.Element' constrained to non-protocol, non-class type 'String'}} expected-note {{use 'T.Element == String' to require 'T.Element' to be 'String'}} {{47-48= ==}}
670+
}
671+
672+
struct A<T: String> {} // expected-error {{type 'T' constrained to non-protocol, non-class type 'String'}}
673+
struct B<T> where T: String {} // expected-error {{type 'T' constrained to non-protocol, non-class type 'String'}}
674+
protocol C {
675+
associatedtype Foo: String // expected-error {{inheritance from non-protocol, non-class type 'String'}} expected-error {{type 'Self.Foo' constrained to non-protocol, non-class type 'String'}}
676+
}
677+
protocol D {
678+
associatedtype Foo where Foo: String // expected-error {{type 'Self.Foo' constrained to non-protocol, non-class type 'String'}}
679+
}

0 commit comments

Comments
 (0)