Skip to content

[Diagnostics] Transform incorrect generic arguments into a contexua… #26716

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 19, 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
17 changes: 6 additions & 11 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -650,10 +650,8 @@ void GenericArgumentsMismatchFailure::emitNoteForMismatch(int position) {
auto genericTypeDecl = paramSourceTy->getAnyGeneric();
auto param = genericTypeDecl->getGenericParams()->getParams()[position];

auto lhs = resolveType(getActual()->getGenericArgs()[position])
->reconstituteSugar(/*recursive=*/false);
auto rhs = resolveType(getRequired()->getGenericArgs()[position])
->reconstituteSugar(/*recursive=*/false);
auto lhs = getActual()->getGenericArgs()[position];
auto rhs = getRequired()->getGenericArgs()[position];

auto noteLocation = param->getLoc();

Expand All @@ -677,8 +675,8 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() {
const auto &last = path.back();
switch (last.getKind()) {
case ConstraintLocator::ContextualType: {
auto purpose = getConstraintSystem().getContextualTypePurpose();
assert(purpose != CTP_Unused);
auto purpose = getContextualTypePurpose();
assert(!(purpose == CTP_Unused && purpose == CTP_CannotFail));
diagnostic = getDiagnosticFor(purpose);
break;
}
Expand All @@ -701,17 +699,14 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() {
}

default:
break;
return false;
}
}

if (!diagnostic)
return false;

emitDiagnostic(
getAnchor()->getLoc(), *diagnostic,
resolveType(getActual())->reconstituteSugar(/*recursive=*/false),
resolveType(getRequired())->reconstituteSugar(/*recursive=*/false));
emitDiagnostic(anchor->getLoc(), *diagnostic, getFromType(), getToType());
emitNotesForMismatches();
return true;
}
Expand Down
86 changes: 45 additions & 41 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,45 +367,6 @@ class MissingConformanceFailure final : public RequirementFailure {
}
};

/// Diagnostics for mismatched generic arguments e.g
/// ```swift
/// struct F<G> {}
/// let _:F<Int> = F<Bool>()
/// ```
class GenericArgumentsMismatchFailure final : public FailureDiagnostic {
BoundGenericType *Actual;
BoundGenericType *Required;
ArrayRef<unsigned> Mismatches;

public:
GenericArgumentsMismatchFailure(Expr *expr, ConstraintSystem &cs,
BoundGenericType *actual,
BoundGenericType *required,
ArrayRef<unsigned> mismatches,
ConstraintLocator *locator)
: FailureDiagnostic(expr, cs, locator), Actual(actual),
Required(required), Mismatches(mismatches) {}

bool diagnoseAsError() override;

private:
void emitNotesForMismatches() {
for (unsigned position : Mismatches) {
emitNoteForMismatch(position);
}
}

void emitNoteForMismatch(int mismatchPosition);

Optional<Diag<Type, Type>> getDiagnosticFor(ContextualTypePurpose context);

/// The actual type being used.
BoundGenericType *getActual() const { return Actual; }

/// The type needed by the generic requirement.
BoundGenericType *getRequired() const { return Required; }
};

/// Diagnose failures related to same-type generic requirements, e.g.
/// ```swift
/// protocol P {
Expand Down Expand Up @@ -723,8 +684,6 @@ class ContextualFailure : public FailureDiagnostic {
Type contextualType);

private:
ContextualTypePurpose getContextualTypePurpose() const { return CTP; }

Type resolve(Type rawType) {
auto type = resolveType(rawType)->getWithoutSpecifierType();
if (auto *BGT = type->getAs<BoundGenericType>()) {
Expand All @@ -749,10 +708,55 @@ class ContextualFailure : public FailureDiagnostic {
bool isIntegerToStringIndexConversion() const;

protected:
ContextualTypePurpose getContextualTypePurpose() const { return CTP; }

static Optional<Diag<Type, Type>>
getDiagnosticFor(ContextualTypePurpose context, bool forProtocol);
};

/// Diagnostics for mismatched generic arguments e.g
/// ```swift
/// struct F<G> {}
/// let _:F<Int> = F<Bool>()
/// ```
class GenericArgumentsMismatchFailure final : public ContextualFailure {
ArrayRef<unsigned> Mismatches;

public:
GenericArgumentsMismatchFailure(Expr *expr, ConstraintSystem &cs,
Type actualType, Type requiredType,
ArrayRef<unsigned> mismatches,
ConstraintLocator *locator)
: ContextualFailure(expr, cs, actualType, requiredType, locator),
Mismatches(mismatches) {
assert(actualType->is<BoundGenericType>());
assert(requiredType->is<BoundGenericType>());
}

bool diagnoseAsError() override;

private:
void emitNotesForMismatches() {
for (unsigned position : Mismatches) {
emitNoteForMismatch(position);
}
}

void emitNoteForMismatch(int mismatchPosition);

Optional<Diag<Type, Type>> getDiagnosticFor(ContextualTypePurpose context);

/// The actual type being used.
BoundGenericType *getActual() const {
return getFromType()->castTo<BoundGenericType>();
}

/// The type needed by the generic requirement.
BoundGenericType *getRequired() const {
return getToType()->castTo<BoundGenericType>();
}
};

/// Diagnose failures related to conversion between throwing function type
/// and non-throwing one e.g.
///
Expand Down
8 changes: 4 additions & 4 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,14 @@ AllowTupleTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
}

bool GenericArgumentsMismatch::diagnose(Expr *root, bool asNote) const {
auto failure = GenericArgumentsMismatchFailure(root, getConstraintSystem(),
getActual(), getRequired(),
getMismatches(), getLocator());
auto &cs = getConstraintSystem();
GenericArgumentsMismatchFailure failure(root, cs, getFromType(), getToType(),
getMismatches(), getLocator());
return failure.diagnose(asNote);
}

GenericArgumentsMismatch *GenericArgumentsMismatch::create(
ConstraintSystem &cs, BoundGenericType *actual, BoundGenericType *required,
ConstraintSystem &cs, Type actual, Type required,
llvm::ArrayRef<unsigned> mismatches, ConstraintLocator *locator) {
unsigned size = totalSizeToAlloc<unsigned>(mismatches.size());
void *mem =
Expand Down
23 changes: 9 additions & 14 deletions lib/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -569,22 +569,21 @@ class RemoveAddressOf final : public ContextualMismatch {
/// let _:F<Int> = F<Bool>()
/// ```
class GenericArgumentsMismatch final
: public ConstraintFix,
: public ContextualMismatch,
private llvm::TrailingObjects<GenericArgumentsMismatch, unsigned> {
friend TrailingObjects;

BoundGenericType *Actual;
BoundGenericType *Required;

unsigned NumMismatches;

protected:
GenericArgumentsMismatch(ConstraintSystem &cs, BoundGenericType *actual,
BoundGenericType *required,
GenericArgumentsMismatch(ConstraintSystem &cs, Type actual, Type required,
llvm::ArrayRef<unsigned> mismatches,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::GenericArgumentsMismatch, locator),
Actual(actual), Required(required), NumMismatches(mismatches.size()) {
: ContextualMismatch(cs, FixKind::GenericArgumentsMismatch, actual,
required, locator),
NumMismatches(mismatches.size()) {
assert(actual->is<BoundGenericType>());
assert(required->is<BoundGenericType>());
std::uninitialized_copy(mismatches.begin(), mismatches.end(),
getMismatchesBuf().begin());
}
Expand All @@ -594,18 +593,14 @@ class GenericArgumentsMismatch final
return "fix generic argument mismatch";
}

BoundGenericType *getActual() const { return Actual; }
BoundGenericType *getRequired() const { return Required; }

ArrayRef<unsigned> getMismatches() const {
return {getTrailingObjects<unsigned>(), NumMismatches};
}

bool diagnose(Expr *root, bool asNote = false) const override;

static GenericArgumentsMismatch *create(ConstraintSystem &cs,
BoundGenericType *actual,
BoundGenericType *required,
static GenericArgumentsMismatch *create(ConstraintSystem &cs, Type actual,
Type required,
llvm::ArrayRef<unsigned> mismatches,
ConstraintLocator *locator);

Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1771,7 +1771,7 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
return result;

auto *fix = GenericArgumentsMismatch::create(
*this, bound1, bound2, mismatches, getConstraintLocator(locator));
*this, type1, type2, mismatches, getConstraintLocator(locator));

if (!recordFix(fix)) {
// Increase the solution's score for each mismtach this fixes.
Expand Down
8 changes: 4 additions & 4 deletions test/Parse/pointer_conversion.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func mutablePointerArguments(_ p: UnsafeMutablePointer<Int>,
var ii: [Int] = [0, 1, 2]
var ff: [Float] = [0, 1, 2]
takesMutablePointer(&ii)
takesMutablePointer(&ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'UnsafeMutablePointer<Int>'}}
takesMutablePointer(&ff) // expected-error{{cannot convert value of type 'Array<Float>' to expected argument type 'UnsafeMutablePointer<Int>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Float' and 'Int') are expected to be equal}}
takesMutablePointer(ii) // expected-error{{cannot convert value of type '[Int]' to expected argument type 'UnsafeMutablePointer<Int>${diag_suffix}'}}
takesMutablePointer(ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'UnsafeMutablePointer<Int>${diag_suffix}'}}
Expand Down Expand Up @@ -153,10 +153,10 @@ func constPointerArguments(_ p: UnsafeMutablePointer<Int>,
var ii: [Int] = [0, 1, 2]
var ff: [Float] = [0, 1, 2]
takesConstPointer(&ii)
takesConstPointer(&ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'UnsafePointer<Int>'}}
takesConstPointer(&ff) // expected-error{{cannot convert value of type 'Array<Float>' to expected argument type 'UnsafePointer<Int>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Float' and 'Int') are expected to be equal}}
takesConstPointer(ii)
takesConstPointer(ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'UnsafePointer<Int>'}}
takesConstPointer(ff) // expected-error{{cannot convert value of type 'Array<Float>' to expected argument type 'UnsafePointer<Int>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Float' and 'Int') are expected to be equal}}
takesConstPointer([0, 1, 2])
// <rdar://problem/22308330> QoI: CSDiags doesn't handle array -> pointer impl conversions well
Expand Down Expand Up @@ -341,7 +341,7 @@ func f23202128() {
UMP(&pipe) // expected-error {{cannot pass immutable value as inout argument: 'pipe' is a 'let' constant}}

var pipe2: [Int] = [0, 0]
UMP(&pipe2) // expected-error {{cannot convert value of type '[Int]' to expected argument type 'UnsafeMutablePointer<Int32>'}}
UMP(&pipe2) // expected-error {{cannot convert value of type 'Array<Int>' to expected argument type 'UnsafeMutablePointer<Int32>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Int' and 'Int32') are expected to be equal}}


Expand Down
4 changes: 2 additions & 2 deletions test/decl/typealias/generic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ let _: GenericClass<Int>.TA = GenericClass<Int>.TA<Float>(a: 1, b: 4.0)
let _: GenericClass<Int>.TA<Float> = GenericClass.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'MyType<Double, Int>' to specified type 'GenericClass<Int>.TA<Float>' (aka 'MyType<Int, Float>')}}
let _: GenericClass<Int>.TA<Float> = GenericClass.TA(a: 1, b: 4.0)

let _: GenericClass<Int>.TA<Float> = GenericClass.TA<Float>(a: 4.0, b: 1) // expected-error {{cannot assign value of type 'MyType<Float, Float>' to type 'MyType<Int, Float>'}}
let _: GenericClass<Int>.TA<Float> = GenericClass.TA<Float>(a: 1, b: 4.0) // expected-error {{cannot assign value of type 'MyType<Float, Float>' to type 'MyType<Int, Float>'}}
let _: GenericClass<Int>.TA<Float> = GenericClass.TA<Float>(a: 4.0, b: 1) // expected-error {{cannot assign value of type 'MyType<Float, Float>' to type 'GenericClass<Int>.TA<Float>' (aka 'MyType<Int, Float>')}}
let _: GenericClass<Int>.TA<Float> = GenericClass.TA<Float>(a: 1, b: 4.0) // expected-error {{cannot assign value of type 'MyType<Float, Float>' to type 'GenericClass<Int>.TA<Float>' (aka 'MyType<Int, Float>')}}

let _: GenericClass<Int>.TA<Float> = GenericClass<Int>.TA(a: 4.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
let _: GenericClass<Int>.TA<Float> = GenericClass<Int>.TA(a: 1, b: 4.0)
Expand Down
6 changes: 3 additions & 3 deletions test/expr/cast/dictionary_downcast.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ if let _ = dictCC as? Dictionary<D, C> { }
if let _ = dictCC as? Dictionary<D, D> { }

// Test dictionary downcasts to unrelated types.
dictCC as Dictionary<D, U> // expected-error{{cannot convert value of type '[C : C]' to type '[D : U]' in coercion}}
dictCC as Dictionary<D, U> // expected-error{{cannot convert value of type '[C : C]' to type 'Dictionary<D, U>' in coercion}}
// expected-note@-1 {{arguments to generic parameter 'Key' ('C' and 'D') are expected to be equal}}
// expected-note@-2 {{arguments to generic parameter 'Value' ('C' and 'U') are expected to be equal}}
dictCC as Dictionary<U, D> // expected-error{{cannot convert value of type '[C : C]' to type '[U : D]' in coercion}}
dictCC as Dictionary<U, D> // expected-error{{cannot convert value of type '[C : C]' to type 'Dictionary<U, D>' in coercion}}
// expected-note@-1 {{arguments to generic parameter 'Key' ('C' and 'U') are expected to be equal}}
// expected-note@-2 {{arguments to generic parameter 'Value' ('C' and 'D') are expected to be equal}}
dictCC as Dictionary<U, U> // expected-error{{cannot convert value of type '[C : C]' to type '[U : U]' in coercion}}
dictCC as Dictionary<U, U> // expected-error{{cannot convert value of type '[C : C]' to type 'Dictionary<U, U>' in coercion}}
// expected-note@-1 {{arguments to generic parameter 'Key' ('C' and 'U') are expected to be equal}}
// expected-note@-2 {{arguments to generic parameter 'Value' ('C' and 'U') are expected to be equal}}

Expand Down
4 changes: 2 additions & 2 deletions test/expr/unary/keypath/salvage-with-other-type-errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// to diagnose other errors in adjacent exprs.

struct P<T: K> { }
// expected-note@-1 {{arguments to generic parameter 'T' ('String' and '_') are expected to be equal}}
// expected-note@-1 {{arguments to generic parameter 'T' ('String' and 'T') are expected to be equal}}

struct S {
init<B>(_ a: P<B>) {
Expand All @@ -28,7 +28,7 @@ struct A {
}

extension A: K {
static let j = S(\A.id + "id") // expected-error {{cannot convert value of type 'P<String>' to expected argument type 'P<_>'}}
static let j = S(\A.id + "id") // expected-error {{cannot convert value of type 'P<String>' to expected argument type 'P<T>'}}
}

// SR-5034
Expand Down