Skip to content

[CSSimplify] Delay inout type to pointer conversion until inout is sufficiently resolved #76439

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
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: 17 additions & 0 deletions lib/Sema/CSBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,23 @@ PotentialBindings::inferFromRelational(Constraint *constraint) {
AdjacentVars.insert({typeVar, constraint});
}

// Infer a binding from `inout $T <convertible to> Unsafe*Pointer<...>?`.
if (first->is<InOutType>() &&
first->getInOutObjectType()->isEqual(TypeVar)) {
if (auto pointeeTy = second->lookThroughAllOptionalTypes()
->getAnyPointerElementType()) {
if (!pointeeTy->isTypeVariableOrMember()) {
// The binding is as a fallback in this case because $T could
// also be Array<X> or C-style pointer.
if (constraint->getKind() >= ConstraintKind::ArgumentConversion)
DelayedBy.push_back(constraint);

return PotentialBinding(pointeeTy, AllowedBindingKind::Exact,
constraint);
}
}
}

return std::nullopt;
}

Expand Down
15 changes: 8 additions & 7 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7683,15 +7683,16 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
// scalar or array.
if (auto inoutType1 = dyn_cast<InOutType>(desugar1)) {
if (!isAutoClosureArgument) {
auto inoutBaseType = inoutType1->getInOutObjectType();
auto inoutBaseType = getFixedTypeRecursive(
inoutType1->getInOutObjectType(), /*wantRValue=*/true);

auto baseIsArray =
getFixedTypeRecursive(inoutBaseType, /*wantRValue=*/true)
->isArrayType();
// Wait until the base type of `inout` is sufficiently resolved
// before making any assessments regarding conversions.
if (inoutBaseType->isTypeVariableOrMember())
return formUnsolvedResult();

auto baseIsArray = inoutBaseType->isArrayType();

// FIXME: If the base is still a type variable, we can't tell
// what to do here. Might have to try \c ArrayToPointer and make
// it more robust.
if (baseIsArray)
conversionsOrFixes.push_back(
ConversionRestrictionKind::ArrayToPointer);
Expand Down
15 changes: 6 additions & 9 deletions test/Constraints/optional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -478,20 +478,17 @@ func rdar75146811() {

var arr: [Double]! = []

test(&arr) // expected-error {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
test(&arr) // Ok
test((&arr)) // expected-error {{'&' may only be used to pass an argument to inout parameter}}
// expected-error@-1 {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
test(&(arr)) // expected-error {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
test(&(arr)) // Ok

test_tuple(&arr, x: 0) // expected-error {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
test_tuple(&arr, x: 0) // Ok
test_tuple((&arr), x: 0) // expected-error {{'&' may only be used to pass an argument to inout parameter}}
// expected-error@-1 {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
test_tuple(&(arr), x: 0) // expected-error {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
test_tuple(&(arr), x: 0) // Ok

test_named(x: &arr) // expected-error {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
test_named(x: &arr) // Ok
test_named(x: (&arr)) // expected-error {{'&' may only be used to pass an argument to inout parameter}}
// expected-error@-1 {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
test_named(x: &(arr)) // expected-error {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
test_named(x: &(arr)) // Ok
}

// rdar://75514153 - Unable to produce a diagnostic for ambiguities related to use of `nil`
Expand Down
18 changes: 18 additions & 0 deletions test/Constraints/valid_pointer_conversions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,21 @@ do {
func rdar68254165(ptr: UnsafeMutablePointer<Int8>) {
_ = String(decodingCString: ptr, as: .utf8) // expected-error {{generic parameter 'Encoding' could not be inferred}}
}

// The base of leading-dot syntax could be inferred through an implicit pointer conversion.
do {
struct S {
static var prop = S()
}

func inference_through_optional(_ ptr: UnsafePointer<S>?) {}

inference_through_optional(&.prop) // Ok

func inference_through_force_unwrap(name: String) {
func test(_: UnsafeMutablePointer<Float>!) {}

var outputs = [String: [Float]]()
test(&outputs[name]!) // Ok
}
}
12 changes: 8 additions & 4 deletions test/Sema/diag_non_ephemeral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,10 @@ takesMutableRaw(&ResilientStruct.staticStoredProperty, 5) // expected-error {{ca
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}}
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}

// FIXME: This should also produce an error.
takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5)

takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}}
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}}
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}

// - Resilient struct or class bases

Expand All @@ -221,8 +223,10 @@ takesRaw(&topLevelP.property) // expected-error {{cannot use inout expression he
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}

// FIXME: This should also produce an error.
takesRaw(&type(of: topLevelP).staticProperty)

takesRaw(&type(of: topLevelP).staticProperty) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}}
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}

takesRaw(&topLevelP[]) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}}
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
Expand Down
10 changes: 6 additions & 4 deletions test/Sema/diag_non_ephemeral_warning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,9 @@ takesMutableRaw(&ResilientStruct.staticStoredProperty, 5) // expected-warning {{
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}}
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}

// FIXME: This should also produce a warning.
takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5)
takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5) // expected-warning {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}}
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}}
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}

// - Resilient struct or class bases

Expand All @@ -221,8 +222,9 @@ takesRaw(&topLevelP.property) // expected-warning {{cannot use inout expression
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}

// FIXME: This should also produce a warning.
takesRaw(&type(of: topLevelP).staticProperty)
takesRaw(&type(of: topLevelP).staticProperty) // expected-warning {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}}
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}

takesRaw(&topLevelP[]) // expected-warning {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}}
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
Expand Down