Skip to content

[GSB] [Diag] Constraint to concrete type using ":" should offer a fix-it #22152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1981,6 +1981,9 @@ ERROR(protocol_composition_one_class,none,
ERROR(requires_conformance_nonprotocol,none,
"type %0 constrained to non-protocol, non-class type %1",
(Type, Type))
NOTE(requires_conformance_nonprotocol_fixit,none,
"use '%0 == %1' to require '%0' to be '%1'",
(StringRef, StringRef))
ERROR(requires_not_suitable_archetype,none,
"type %0 in conformance requirement does not refer to a "
"generic parameter or associated type",
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/GenericSignatureBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ class GenericSignatureBuilder {
/// \c ConstraintResult::Unresolved so the caller knows what happened.
GenerateUnresolved = 1,
};

/// The set of constraints that are invalid because the constraint
/// type isn't constrained to a protocol or a class
std::vector<Constraint<Type>> invalidIsaConstraints;

private:
class InferRequirementsWalker;
Expand Down
47 changes: 45 additions & 2 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4629,8 +4629,14 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement(
->getDependentType(getGenericParams());

Impl->HadAnyError = true;
Diags.diagnose(source.getLoc(), diag::requires_conformance_nonprotocol,
subjectType, constraintType);

if (subjectType->is<DependentMemberType>()) {
subjectType = resolveDependentMemberTypes(*this, subjectType);
}

auto invalidConstraint = Constraint<Type>(
{subject, constraintType, source.getSource(*this, subjectType)});
invalidIsaConstraints.push_back(invalidConstraint);
}

return ConstraintResult::Conflicting;
Expand Down Expand Up @@ -5750,6 +5756,43 @@ GenericSignatureBuilder::finalize(SourceLoc loc,
}
}
}

// Emit a diagnostic if we recorded any constraints where the constraint
// type was not constrained to a protocol or class. Provide a fix-it if
// allowConcreteGenericParams is true or the subject type is a member type.
if (!invalidIsaConstraints.empty()) {
for (auto constraint : invalidIsaConstraints) {
auto subjectType = constraint.getSubjectDependentType(getGenericParams());
auto constraintType = constraint.value;
auto source = constraint.source;
auto loc = source->getLoc();

Diags.diagnose(loc, diag::requires_conformance_nonprotocol,
subjectType, constraintType);

auto getNameWithoutSelf = [&](std::string subjectTypeName) {
std::string selfSubstring = "Self.";

if (subjectTypeName.rfind(selfSubstring, 0) == 0) {
return subjectTypeName.erase(0, selfSubstring.length());
}

return subjectTypeName;
};

if (allowConcreteGenericParams ||
(subjectType->is<DependentMemberType>() &&
!source->isProtocolRequirement())) {
auto subjectTypeName = subjectType.getString();
auto subjectTypeNameWithoutSelf = getNameWithoutSelf(subjectTypeName);
Diags.diagnose(loc, diag::requires_conformance_nonprotocol_fixit,
subjectTypeNameWithoutSelf, constraintType.getString())
.fixItReplace(loc, " == ");
}
}

invalidIsaConstraints.clear();
}
}

/// Turn a requirement right-hand side into an unresolved type.
Expand Down
41 changes: 40 additions & 1 deletion test/Constraints/generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -637,4 +637,43 @@ func compare<C: Collection, Key: Hashable, Value: Equatable>(c: C)
-> Bool where C.Element == (key: Key, value: Value)
{
_ = Dictionary(uniqueKeysWithValues: Array(c))
}
}

// https://bugs.swift.org/browse/SR-7984
struct SR_7984<Bar> {
func doSomething() {}
}

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= ==}}

protocol SR_7984_Proto {
associatedtype Bar
}

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= ==}}

protocol SR_7984_HasFoo {
associatedtype Foo
}
protocol SR_7984_HasAssoc {
associatedtype Assoc: SR_7984_HasFoo
}

struct SR_7984_X<T: SR_7984_HasAssoc> {}
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= ==}}

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= ==}}
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= ==}}

protocol SR_7984_P {
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= ==}}
}

struct A<T: String> {} // expected-error {{type 'T' constrained to non-protocol, non-class type 'String'}}
struct B<T> where T: String {} // expected-error {{type 'T' constrained to non-protocol, non-class type 'String'}}
protocol C {
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'}}
}
protocol D {
associatedtype Foo where Foo: String // expected-error {{type 'Self.Foo' constrained to non-protocol, non-class type 'String'}}
}