Skip to content

[ConstraintSystem] Extend generic argument mismatch fix to all pointer conversions #26996

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 2 commits into from
Sep 3, 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
45 changes: 0 additions & 45 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4389,52 +4389,7 @@ bool FailureDiagnosis::visitAssignExpr(AssignExpr *assignExpr) {
return false;
}


/// Return true if this type is known to be an ArrayType.
static bool isKnownToBeArrayType(Type ty) {
if (!ty) return false;

auto bgt = ty->getAs<BoundGenericType>();
if (!bgt) return false;

auto &ctx = bgt->getASTContext();
return bgt->getDecl() == ctx.getArrayDecl();
}

bool FailureDiagnosis::visitInOutExpr(InOutExpr *IOE) {
// If we have a contextual type, it must be an inout type.
auto contextualType = CS.getContextualType();
if (contextualType) {
// If the contextual type is one of the UnsafePointer<T> types, then the
// contextual type of the subexpression must be T.
Type unwrappedType = contextualType;
if (auto unwrapped = contextualType->getOptionalObjectType())
unwrappedType = unwrapped;

if (auto pointerEltType = unwrappedType->getAnyPointerElementType()) {

// If the element type is Void, then we allow any input type, since
// everything is convertible to UnsafeRawPointer
if (pointerEltType->isVoid())
contextualType = Type();
else
contextualType = pointerEltType;

// Furthermore, if the subexpr type is already known to be an array type,
// then we must have an attempt at an array to pointer conversion.
if (isKnownToBeArrayType(CS.getType(IOE->getSubExpr()))) {
contextualType = ArraySliceType::get(contextualType);
}
} else if (contextualType->is<InOutType>()) {
contextualType = contextualType->getInOutObjectType();
}
}

if (!typeCheckChildIndependently(IOE->getSubExpr(), contextualType,
CS.getContextualTypePurpose(),
TCC_AllowLValue)) {
return true;
}
return false;
}

Expand Down
80 changes: 51 additions & 29 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2558,8 +2558,15 @@ bool ConstraintSystem::repairFailures(
// `&`.
if (lhs->is<LValueType>() &&
(rhs->is<InOutType>() || rhs->getAnyPointerElementType())) {
auto result = matchTypes(InOutType::get(lhs->getRValueType()), rhs,
ConstraintKind::ArgumentConversion,
auto baseType = rhs->is<InOutType>() ? rhs->getInOutObjectType()
: rhs->getAnyPointerElementType();

// Let's use `BindToPointer` constraint here to match up base types
// of implied `inout` argument and `inout` or pointer parameter.
// This helps us to avoid implicit conversions associated with
// `ArgumentConversion` constraint.
auto result = matchTypes(lhs->getRValueType(), baseType,
ConstraintKind::BindToPointerType,
TypeMatchFlags::TMF_ApplyingFix, locator);

if (result.isSuccess()) {
Expand Down Expand Up @@ -6958,6 +6965,44 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(

TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

auto matchPointerBaseTypes = [&](Type baseType1,
Type baseType2) -> SolutionKind {
if (restriction != ConversionRestrictionKind::PointerToPointer)
increaseScore(ScoreKind::SK_ValueToPointerConversion);

auto result =
matchTypes(baseType1, baseType2, ConstraintKind::BindToPointerType,
subflags, locator);

if (!(result.isFailure() && shouldAttemptFixes()))
return result;

BoundGenericType *ptr1 = nullptr;
BoundGenericType *ptr2 = nullptr;

switch (restriction) {
case ConversionRestrictionKind::ArrayToPointer:
case ConversionRestrictionKind::InoutToPointer: {
ptr2 = type2->lookThroughAllOptionalTypes()->castTo<BoundGenericType>();
ptr1 = BoundGenericType::get(ptr2->getDecl(), ptr2->getParent(),
{baseType1});
break;
}

case ConversionRestrictionKind::PointerToPointer:
ptr1 = type1->castTo<BoundGenericType>();
ptr2 = type2->castTo<BoundGenericType>();
break;

default:
return SolutionKind::Error;
}

auto *fix = GenericArgumentsMismatch::create(*this, ptr1, ptr2, {0},
getConstraintLocator(locator));
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
};

switch (restriction) {
// for $< in { <, <c, <oc }:
// T_i $< U_i ===> (T_i...) $< (U_i...)
Expand Down Expand Up @@ -7077,23 +7122,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
auto baseType1 = getFixedTypeRecursive(*isArrayType(obj1), false);
auto baseType2 = getBaseTypeForPointer(*this, t2);

increaseScore(ScoreKind::SK_ValueToPointerConversion);
auto result = matchTypes(baseType1, baseType2,
ConstraintKind::BindToPointerType,
subflags, locator);

if (!(result.isFailure() && shouldAttemptFixes()))
return result;

auto *arrTy = obj1->getAs<BoundGenericType>();
// Let's dig out underlying pointer type since it could
// be wrapped into N (implicit) optionals.
auto *ptrTy =
type2->lookThroughAllOptionalTypes()->getAs<BoundGenericType>();

auto *fix = GenericArgumentsMismatch::create(*this, arrTy, ptrTy, {0},
getConstraintLocator(locator));
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
return matchPointerBaseTypes(baseType1, baseType2);
}

// String ===> UnsafePointer<[U]Int8>
Expand Down Expand Up @@ -7146,13 +7175,8 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(

auto baseType1 = type1->getInOutObjectType();
auto baseType2 = getBaseTypeForPointer(*this, t2);

// Set up the disjunction for the array or scalar cases.

increaseScore(ScoreKind::SK_ValueToPointerConversion);
return matchTypes(baseType1, baseType2,
ConstraintKind::BindToPointerType,
subflags, locator);
return matchPointerBaseTypes(baseType1, baseType2);
}

// T <p U ===> UnsafeMutablePointer<T> <a UnsafeMutablePointer<U>
Expand All @@ -7162,10 +7186,8 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(

Type baseType1 = getBaseTypeForPointer(*this, t1);
Type baseType2 = getBaseTypeForPointer(*this, t2);

return matchTypes(baseType1, baseType2,
ConstraintKind::BindToPointerType,
subflags, locator);

return matchPointerBaseTypes(baseType1, baseType2);
}

// T < U or T is bridged to V where V < U ===> Array<T> <c Array<U>
Expand Down
6 changes: 4 additions & 2 deletions test/ClangImporter/objc_id_as_any.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ idLover.takesId(ArbitraryThing())
var x: AnyObject = NSObject()
idLover.takesArray(ofId: &x)
var xAsAny = x as Any
idLover.takesArray(ofId: &xAsAny) // expected-error{{argument type 'Any' does not conform to expected type 'AnyObject'}}
idLover.takesArray(ofId: &xAsAny) // expected-error{{cannot convert value of type 'UnsafePointer<Any>' to expected argument type 'UnsafePointer<AnyObject>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Any' and 'AnyObject') are expected to be equal}}

var y: Any = NSObject()
idLover.takesArray(ofId: &y) // expected-error{{argument type 'Any' does not conform to expected type 'AnyObject'}}
idLover.takesArray(ofId: &y) // expected-error{{cannot convert value of type 'UnsafePointer<Any>' to expected argument type 'UnsafePointer<AnyObject>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Any' and 'AnyObject') are expected to be equal}}

idLover.takesId(x)
idLover.takesId(y)
Expand Down
26 changes: 16 additions & 10 deletions test/Parse/pointer_conversion.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,20 @@ func mutablePointerArguments(_ p: UnsafeMutablePointer<Int>,
var i: Int = 0
var f: Float = 0
takesMutablePointer(&i)
takesMutablePointer(&f) // expected-error{{cannot convert value of type 'Float' to expected argument type 'Int'}}
takesMutablePointer(&f) // expected-error{{cannot convert value of type 'UnsafeMutablePointer<Float>' to expected argument type 'UnsafeMutablePointer<Int>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Float' and 'Int') are expected to be equal}}
takesMutablePointer(i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'UnsafeMutablePointer<Int>${diag_suffix}'}}
takesMutablePointer(f) // expected-error{{cannot convert value of type 'Float' to expected argument type 'UnsafeMutablePointer<Int>${diag_suffix}'}}
var ii: [Int] = [0, 1, 2]
var ff: [Float] = [0, 1, 2]
takesMutablePointer(&ii)
takesMutablePointer(&ff) // expected-error{{cannot convert value of type 'Array<Float>' to expected argument type 'UnsafeMutablePointer<Int>'}}
takesMutablePointer(&ff) // expected-error{{cannot convert value of type 'UnsafeMutablePointer<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}'}}

takesMutableArrayPointer(&i) // expected-error{{cannot convert value of type 'Int' to expected argument type '[Int]'}}
takesMutableArrayPointer(&i) // expected-error{{cannot convert value of type 'UnsafeMutablePointer<Int>' to expected argument type 'UnsafeMutablePointer<[Int]>'}}
//expected-note@-1 {{arguments to generic parameter 'Pointee' ('Int' and '[Int]') are expected to be equal}}
takesMutableArrayPointer(&ii)

// We don't allow these conversions outside of function arguments.
Expand Down Expand Up @@ -149,14 +151,15 @@ func constPointerArguments(_ p: UnsafeMutablePointer<Int>,
var i: Int = 0
var f: Float = 0
takesConstPointer(&i)
takesConstPointer(&f) // expected-error{{cannot convert value of type 'Float' to expected argument type 'Int'}}
takesConstPointer(&f) // expected-error{{cannot convert value of type 'UnsafePointer<Float>' to expected argument type 'UnsafePointer<Int>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Float' and 'Int') are expected to be equal}}
var ii: [Int] = [0, 1, 2]
var ff: [Float] = [0, 1, 2]
takesConstPointer(&ii)
takesConstPointer(&ff) // expected-error{{cannot convert value of type 'Array<Float>' to expected argument type 'UnsafePointer<Int>'}}
takesConstPointer(&ff) // expected-error{{cannot convert value of type 'UnsafePointer<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 'Array<Float>' to expected argument type 'UnsafePointer<Int>'}}
takesConstPointer(ff) // expected-error{{cannot convert value of type 'UnsafePointer<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 @@ -258,9 +261,11 @@ func stringArguments(_ s: String) {
takesMutableVoidPointer(s) // expected-error{{cannot convert value of type 'String' to expected argument type 'UnsafeMutableRawPointer${diag_suffix}'}}
takesMutableRawPointer(s) // expected-error{{cannot convert value of type 'String' to expected argument type 'UnsafeMutableRawPointer${diag_suffix}'}}
takesMutableInt8Pointer(s) // expected-error{{cannot convert value of type 'String' to expected argument type 'UnsafeMutablePointer<Int8>${diag_suffix}'}}
takesMutableInt8Pointer(&s) // expected-error{{cannot convert value of type 'String' to expected argument type 'Int8'}}
takesMutableInt8Pointer(&s) // expected-error{{cannot convert value of type 'UnsafeMutablePointer<String>' to expected argument type 'UnsafeMutablePointer<Int8>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('String' and 'Int8') are expected to be equal}}
takesMutablePointer(s) // expected-error{{cannot convert value of type 'String' to expected argument type 'UnsafeMutablePointer<Int>${diag_suffix}'}}
takesMutablePointer(&s) // expected-error{{cannot convert value of type 'String' to expected argument type 'Int'}}
takesMutablePointer(&s) // expected-error{{cannot convert value of type 'UnsafeMutablePointer<String>' to expected argument type 'UnsafeMutablePointer<Int>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('String' and 'Int') are expected to be equal}}
}


Expand Down Expand Up @@ -319,7 +324,8 @@ func addressConversion(p: UnsafeMutablePointer<Int>, x: Int) {
func f19478919() {
var viewport: Int = 1 // intentionally incorrect type, not Int32
func GLKProject(_ a : UnsafeMutablePointer<Int32>) {}
GLKProject(&viewport) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Int32'}}
GLKProject(&viewport) // expected-error {{cannot convert value of type 'UnsafeMutablePointer<Int>' to expected argument type 'UnsafeMutablePointer<Int32>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Int' and 'Int32') are expected to be equal}}

func GLKProjectUP(_ a : UnsafePointer<Int32>) {}
func UP_Void(_ a : UnsafeRawPointer) {}
Expand All @@ -341,7 +347,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 'Array<Int>' to expected argument type 'UnsafeMutablePointer<Int32>'}}
UMP(&pipe2) // expected-error {{cannot convert value of type 'UnsafeMutablePointer<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
10 changes: 6 additions & 4 deletions test/Parse/pointer_conversion_objc.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ func autoreleasingPointerArguments(p: UnsafeMutablePointer<Int>,
takesAutoreleasingPointer(&c)
takesAutoreleasingPointer(c) // expected-error{{cannot convert value of type 'C' to expected argument type 'AutoreleasingUnsafeMutablePointer<C>${diag_suffix}'}}
var d: D = D()
takesAutoreleasingPointer(&d) // expected-error{{cannot convert value of type 'D' to expected argument type 'C'}}
takesAutoreleasingPointer(&d) // expected-error{{cannot convert value of type 'AutoreleasingUnsafeMutablePointer<D>' to expected argument type 'AutoreleasingUnsafeMutablePointer<C>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('D' and 'C') are expected to be equal}}
takesAutoreleasingPointer(d) // expected-error{{cannot convert value of type 'D' to expected argument type 'AutoreleasingUnsafeMutablePointer<C>${diag_suffix}'}}
var cc: [C] = [C(), C()]
var dd: [D] = [D(), D()]
takesAutoreleasingPointer(&cc) // expected-error{{cannot convert value of type '[C]' to expected argument type 'C'}}
takesAutoreleasingPointer(&dd) // expected-error{{cannot convert value of type '[D]' to expected argument type 'C'}}

takesAutoreleasingPointer(&cc) // expected-error{{cannot convert value of type 'AutoreleasingUnsafeMutablePointer<[C]>' to expected argument type 'AutoreleasingUnsafeMutablePointer<C>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('[C]' and 'C') are expected to be equal}}
takesAutoreleasingPointer(&dd) // expected-error{{cannot convert value of type 'AutoreleasingUnsafeMutablePointer<[D]>' to expected argument type 'AutoreleasingUnsafeMutablePointer<C>'}}
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('[D]' and 'C') are expected to be equal}}
let _: AutoreleasingUnsafeMutablePointer<C> = &c // expected-error {{use of extraneous '&'}}
}