Skip to content

Disallow some implicit pointer conversions in autoclosures. #16623

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
May 16, 2018
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
5 changes: 3 additions & 2 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -673,8 +673,9 @@ class alignas(1 << TypeAlignInBits) TypeBase {
/// Break an existential down into a set of constraints.
ExistentialLayout getExistentialLayout();

/// Determines the element type of a known *UnsafeMutablePointer
/// variant, or returns null if the type is not a pointer.
/// Determines the element type of a known
/// [Autoreleasing]Unsafe[Mutable][Raw]Pointer variant, or returns null if the
/// type is not a pointer.
Type getAnyPointerElementType(PointerTypeKind &PTK);
Type getAnyPointerElementType() {
PointerTypeKind Ignore;
Expand Down
7 changes: 4 additions & 3 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -6889,8 +6889,9 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
->castTo<FunctionType>();

// Convert the value to the expected result type of the function.
expr = coerceToType(expr, toFunc->getResult(),
locator.withPathElement(ConstraintLocator::Load));
expr = coerceToType(
expr, toFunc->getResult(),
locator.withPathElement(ConstraintLocator::AutoclosureResult));

// We'll set discriminator values on all the autoclosures in a
// later pass.
Expand Down
8 changes: 5 additions & 3 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -194,11 +194,13 @@ void constraints::simplifyLocator(Expr *&anchor,

break;

case ConstraintLocator::Load:
case ConstraintLocator::AutoclosureResult:
case ConstraintLocator::RvalueAdjustment:
case ConstraintLocator::ScalarToTuple:
case ConstraintLocator::UnresolvedMember:
// Loads, rvalue adjustment, and scalar-to-tuple conversions are implicit.
// Arguments in autoclosure positions, rvalue adjustments, and
// scalar-to-tuple conversions, and unresolved members are
// implicit.
path = path.slice(1);
continue;

Expand Down
73 changes: 42 additions & 31 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2161,10 +2161,18 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
// an implicit closure.
if (auto function2 = type2->getAs<FunctionType>()) {
if (function2->isAutoClosure())
return matchTypes(type1, function2->getResult(), kind, subflags,
locator.withPathElement(ConstraintLocator::Load));
return matchTypes(
type1, function2->getResult(), kind, subflags,
locator.withPathElement(ConstraintLocator::AutoclosureResult));
}

// It is never legal to form an autoclosure that results in these
// implicit conversions to pointer types.
bool isAutoClosureArgument = false;
if (auto last = locator.last())
if (last->getKind() == ConstraintLocator::AutoclosureResult)
isAutoClosureArgument = true;

// Pointer arguments can be converted from pointer-compatible types.
if (kind >= ConstraintKind::ArgumentConversion) {
Type unwrappedType2 = type2;
Expand All @@ -2183,23 +2191,24 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
case PTK_UnsafeMutablePointer:
// UnsafeMutablePointer can be converted from an inout reference to a
// scalar or array.
if (auto inoutType1 = dyn_cast<InOutType>(desugar1)) {
auto inoutBaseType = inoutType1->getInOutObjectType();

Type simplifiedInoutBaseType =
getFixedTypeRecursive(inoutBaseType,
kind == ConstraintKind::Equal,
isArgumentTupleConversion);

// 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 (isArrayType(simplifiedInoutBaseType)) {
if (!isAutoClosureArgument) {
if (auto inoutType1 = dyn_cast<InOutType>(desugar1)) {
auto inoutBaseType = inoutType1->getInOutObjectType();

Type simplifiedInoutBaseType = getFixedTypeRecursive(
inoutBaseType, kind == ConstraintKind::Equal,
isArgumentTupleConversion);

// 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 (isArrayType(simplifiedInoutBaseType)) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::ArrayToPointer);
}
conversionsOrFixes.push_back(
ConversionRestrictionKind::ArrayToPointer);
ConversionRestrictionKind::InoutToPointer);
}
conversionsOrFixes.push_back(
ConversionRestrictionKind::InoutToPointer);
}

if (!flags.contains(TMF_ApplyingOperatorParameter) &&
Expand Down Expand Up @@ -2247,20 +2256,22 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
// AutoreleasingUnsafeMutablePointer.
if (pointerKind == PTK_UnsafePointer
|| pointerKind == PTK_UnsafeRawPointer) {
if (isArrayType(type1)) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::ArrayToPointer);
}

// The pointer can be converted from a string, if the element type
// is compatible.
if (type1->isEqual(TC.getStringType(DC))) {
auto baseTy = getFixedTypeRecursive(pointeeTy, false);

if (baseTy->isTypeVariableOrMember() ||
isStringCompatiblePointerBaseType(TC, DC, baseTy))
if (!isAutoClosureArgument) {
if (isArrayType(type1)) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::StringToPointer);
ConversionRestrictionKind::ArrayToPointer);
}

// The pointer can be converted from a string, if the element
// type is compatible.
if (type1->isEqual(TC.getStringType(DC))) {
auto baseTy = getFixedTypeRecursive(pointeeTy, false);

if (baseTy->isTypeVariableOrMember() ||
isStringCompatiblePointerBaseType(TC, DC, baseTy))
conversionsOrFixes.push_back(
ConversionRestrictionKind::StringToPointer);
}
}

if (type1IsPointer && optionalityMatches &&
Expand All @@ -2276,7 +2287,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
case PTK_AutoreleasingUnsafeMutablePointer:
// PTK_AutoreleasingUnsafeMutablePointer can be converted from an
// inout reference to a scalar.
if (type1->is<InOutType>()) {
if (!isAutoClosureArgument && type1->is<InOutType>()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::InoutToPointer);
}
Expand Down
8 changes: 4 additions & 4 deletions lib/Sema/ConstraintLocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -69,7 +69,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
case GeneratorElementType:
case ArrayElementType:
case ScalarToTuple:
case Load:
case AutoclosureResult:
case GenericArgument:
case NamedTupleElement:
case TupleElement:
Expand Down Expand Up @@ -173,8 +173,8 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
out << "instance type";
break;

case Load:
out << "load";
case AutoclosureResult:
out << "@autoclosure result";
break;

case Member:
Expand Down
11 changes: 6 additions & 5 deletions lib/Sema/ConstraintLocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -111,8 +111,9 @@ class ConstraintLocator : public llvm::FoldingSetNode {
ArrayElementType,
/// \brief The scalar type of a tuple type.
ScalarToTuple,
/// \brief The load of an lvalue.
Load,
/// \brief An argument passed in an autoclosure parameter
/// position, which must match the autoclosure return type.
AutoclosureResult,
/// The requirement that we're matching during protocol conformance
/// checking.
Requirement,
Expand Down Expand Up @@ -159,7 +160,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
case GeneratorElementType:
case ArrayElementType:
case ScalarToTuple:
case Load:
case AutoclosureResult:
case Requirement:
case Witness:
case OpenedGeneric:
Expand Down Expand Up @@ -205,7 +206,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
case ClosureResult:
case ConstructorMember:
case InstanceType:
case Load:
case AutoclosureResult:
case OptionalPayload:
case Member:
case MemberRefBase:
Expand Down
40 changes: 40 additions & 0 deletions test/Constraints/invalid_implicit_conversions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// RUN: %target-typecheck-verify-swift

func takesAutoclosure<T>(_ lhs: T, _ rhs: @autoclosure () throws -> T) {}

func test(
_ rawPtr: UnsafeRawPointer,
_ mutRawPtr: UnsafeMutableRawPointer,
_ mutPtr: UnsafeMutablePointer<Int>,
_ ptr: UnsafePointer<Int>,
_ ptrI8: UnsafePointer<Int8>,
_ ptrU8: UnsafePointer<UInt8>,
_ ptrVoid: UnsafePointer<Void> // expected-warning {{UnsafePointer<Void> has been replaced by UnsafeRawPointer}}
) {
var i: Int = 0
var a: [Int] = [0]
let s = "string"

takesAutoclosure(rawPtr, &i) // expected-error {{'&' used with non-inout argument of type 'Int'}}
takesAutoclosure(mutRawPtr, &i) // expected-error {{'&' used with non-inout argument of type 'Int'}}
takesAutoclosure(mutPtr, &i) // expected-error {{'&' used with non-inout argument of type 'Int'}}
takesAutoclosure(ptr, &i) // expected-error {{'&' used with non-inout argument of type 'Int'}}
takesAutoclosure(rawPtr, &a) // expected-error {{'&' used with non-inout argument of type '[Int]'}}
takesAutoclosure(mutRawPtr, &a) // expected-error {{'&' used with non-inout argument of type '[Int]'}}
takesAutoclosure(mutPtr, &a) // expected-error {{'&' used with non-inout argument of type '[Int]'}}
takesAutoclosure(ptr, &a) // expected-error {{'&' used with non-inout argument of type '[Int]'}}

takesAutoclosure(rawPtr, a) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafeRawPointer, [Int])'}}
// expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}}
takesAutoclosure(ptr, a) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafePointer<Int>, [Int])'}}
// expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}}

takesAutoclosure(rawPtr, s) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafeRawPointer, String)'}}
// expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}}
takesAutoclosure(ptrI8, s) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafePointer<Int8>, String)'}}
// expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}}
takesAutoclosure(ptrU8, s) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafePointer<UInt8>, String)'}}
// expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}}
takesAutoclosure(ptrVoid, s) // expected-error {{cannot invoke 'takesAutoclosure' with an argument list of type '(UnsafePointer<Void>, String)'}}
// expected-note@-1 {{expected an argument list of type '(T, @autoclosure () throws -> T)'}}
}
49 changes: 49 additions & 0 deletions test/Constraints/valid_implicit_conversions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// RUN: %target-typecheck-verify-swift

func takesAutoclosure<T>(_ lhs: T, _ rhs: @autoclosure () throws -> T) {}
func takesUnsafeRawPointer(_ ptr: UnsafeRawPointer) {}
func takesUnsafeMutableRawPointer(_ ptr: UnsafeMutableRawPointer) {}
func takesUnsafePointer<T>(_ ptr: UnsafePointer<T>) {}
func takesUnsafeMutablePointer<T>(_ ptr: UnsafeMutablePointer<T>) {}
func takesUnsafePointerInt8(_ ptr: UnsafePointer<Int8>) {}
func takesUnsafePointerUInt8(_ ptr: UnsafePointer<UInt8>) {}
func takesUnsafePointerVoid(_ ptr: UnsafePointer<Void>) {} // expected-warning {{UnsafePointer<Void> has been replaced by UnsafeRawPointer}}

func test(
_ rawPtr: UnsafeRawPointer,
_ mutRawPtr: UnsafeMutableRawPointer,
_ mutPtr: UnsafeMutablePointer<Int>,
_ ptr: UnsafePointer<Int>
) {
var i: Int = 0
var a: [Int] = [0]
let s = "string"

takesUnsafeRawPointer(&i)
takesUnsafeMutableRawPointer(&i)
takesUnsafeMutablePointer(&i)
takesUnsafePointer(&i)
takesUnsafeRawPointer(&a)
takesUnsafeMutableRawPointer(&a)
takesUnsafeMutablePointer(&a)
takesUnsafePointer(&a)

takesUnsafeRawPointer(mutPtr)
takesUnsafeMutableRawPointer(mutPtr)
takesUnsafePointer(mutPtr)

takesUnsafeRawPointer(mutRawPtr)

takesUnsafeRawPointer(a)
takesUnsafePointer(a)

takesAutoclosure(rawPtr, mutPtr)
takesAutoclosure(mutRawPtr, mutPtr)
takesAutoclosure(ptr, mutPtr)
takesAutoclosure(rawPtr, mutRawPtr)

takesUnsafeRawPointer(s)
takesUnsafePointerInt8(s)
takesUnsafePointerUInt8(s)
takesUnsafePointerVoid(s)
}