Skip to content

[QoI] Improve diagnostics for misaligned archetypes with the same name in calls #11281

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 1 commit into from
Aug 1, 2017
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
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ ERROR(cannot_convert_default_arg_value_nil,none,
ERROR(cannot_convert_argument_value,none,
"cannot convert value of type %0 to expected argument type %1",
(Type,Type))
ERROR(cannot_convert_argument_value_generic,none,
"cannot convert value of type %0 (%1) to expected argument type %2 (%3)",
(Type, StringRef, Type, StringRef))

ERROR(cannot_convert_argument_value_protocol,none,
"argument type %0 does not conform to expected type %1", (Type, Type))
ERROR(cannot_convert_partial_argument_value_protocol,none,
Expand Down Expand Up @@ -2547,6 +2551,8 @@ ERROR(generic_type_requires_arguments,none,
"reference to generic type %0 requires arguments in <...>", (Type))
NOTE(generic_type_declared_here,none,
"generic type %0 declared here", (Identifier))
NOTE(descriptive_generic_type_declared_here,none,
"%0 declared here", (StringRef))

WARNING(use_of_void_pointer,none,
"Unsafe%0Pointer<Void> has been replaced by Unsafe%0RawPointer", (StringRef))
Expand Down
87 changes: 82 additions & 5 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1860,6 +1860,7 @@ suggestPotentialOverloads(SourceLoc loc, bool isResult) {
/// archetype that has argument type errors, diagnose that error and
/// return true.
bool CalleeCandidateInfo::diagnoseGenericParameterErrors(Expr *badArgExpr) {
TypeChecker &TC = CS.TC;
Type argType = CS.getType(badArgExpr);

// FIXME: For protocol argument types, could add specific error
Expand All @@ -1876,23 +1877,70 @@ bool CalleeCandidateInfo::diagnoseGenericParameterErrors(Expr *badArgExpr) {
argType, archetypesMap))
return false;

auto getGenericTypeDecl = [&](ArchetypeType *archetype) -> ValueDecl * {
auto *env = archetype->getGenericEnvironment();
auto paramType = env->mapTypeOutOfContext(archetype);

if (auto *GTPT = paramType->getAs<GenericTypeParamType>())
return GTPT->getDecl();

if (auto *DMT = paramType->getAs<DependentMemberType>())
return DMT->getAssocType();

return nullptr;
};

auto describeGenericType = [&](ValueDecl *genericParam,
bool includeName = false) -> std::string {
if (!genericParam)
return "";

Decl *parent = nullptr;
if (auto *AT = dyn_cast<AssociatedTypeDecl>(genericParam)) {
parent = AT->getProtocol();
} else {
auto *dc = genericParam->getDeclContext();
parent = dc->getInnermostDeclarationDeclContext();
}

if (!parent)
return "";

llvm::SmallString<64> result;
llvm::raw_svector_ostream OS(result);

OS << Decl::getDescriptiveKindName(genericParam->getDescriptiveKind());

if (includeName && genericParam->hasName())
OS << " '" << genericParam->getBaseName() << "'";

OS << " of ";
OS << Decl::getDescriptiveKindName(parent->getDescriptiveKind());
if (auto *decl = dyn_cast<ValueDecl>(parent)) {
if (decl->hasName())
OS << " '" << decl->getFullName() << "'";
}

return OS.str();
};

for (auto pair : archetypesMap) {
auto archetype = pair.first->castTo<ArchetypeType>();
auto paramArchetype = pair.first->castTo<ArchetypeType>();
auto substitution = pair.second;

// FIXME: Add specific error for not subclass, if the archetype has a superclass?

// Check for optional near miss.
if (auto argOptType = substitution->getOptionalObjectType()) {
if (CS.TC.isSubstitutableFor(argOptType, archetype, CS.DC)) {
if (CS.TC.isSubstitutableFor(argOptType, paramArchetype, CS.DC)) {
CS.TC.diagnose(badArgExpr->getLoc(), diag::missing_unwrap_optional,
argType);
foundFailure = true;
continue;
break;
}
}
for (auto proto : archetype->getConformsTo()) {

for (auto proto : paramArchetype->getConformsTo()) {
if (!CS.TC.conformsToProtocol(substitution, proto, CS.DC,
ConformanceCheckFlags::InExpression)) {
if (substitution->isEqual(argType)) {
Expand All @@ -1905,9 +1953,38 @@ bool CalleeCandidateInfo::diagnoseGenericParameterErrors(Expr *badArgExpr) {
argType, substitution, proto->getDeclaredType());
}
foundFailure = true;
break;
}
}

if (auto *argArchetype = substitution->getAs<ArchetypeType>()) {
// Produce this diagnostic only if the names
// of the generic parameters are the same.
if (argArchetype->getName() != paramArchetype->getName())
continue;

auto *paramDecl = getGenericTypeDecl(paramArchetype);
auto *argDecl = getGenericTypeDecl(argArchetype);

if (!paramDecl || !argDecl)
continue;

TC.diagnose(badArgExpr->getLoc(),
diag::cannot_convert_argument_value_generic, argArchetype,
describeGenericType(argDecl), paramArchetype,
describeGenericType(paramDecl));

TC.diagnose(paramDecl, diag::descriptive_generic_type_declared_here,
describeGenericType(paramDecl, true));

TC.diagnose(argDecl, diag::descriptive_generic_type_declared_here,
describeGenericType(argDecl, true));

foundFailure = true;
break;
}
}

return foundFailure;
}

Expand Down
16 changes: 16 additions & 0 deletions test/Constraints/generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,19 @@ public struct S5 {
g(models: arr)
}
}

// rdar://problem/24329052 - QoI: call argument archetypes not lining up leads to ambiguity errors

struct S_24329052<T> { // expected-note {{generic parameter 'T' of generic struct 'S_24329052' declared here}}
var foo: (T) -> Void
// expected-note@+1 {{generic parameter 'T' of instance method 'bar(_:)' declared here}}
func bar<T>(_ v: T) { foo(v) }
// expected-error@-1 {{cannot convert value of type 'T' (generic parameter of instance method 'bar(_:)') to expected argument type 'T' (generic parameter of generic struct 'S_24329052')}}
}

extension Sequence {
var rdar24329052: (Element) -> Void { fatalError() }
// expected-note@+1 {{generic parameter 'Element' of instance method 'foo24329052(_:)' declared here}}
func foo24329052<Element>(_ v: Element) { rdar24329052(v) }
// expected-error@-1 {{cannot convert value of type 'Element' (generic parameter of instance method 'foo24329052(_:)') to expected argument type 'Self.Element' (associated type of protocol 'Sequence')}}
}