Skip to content

Warn on implicit raw pointer conversion to non-bitwise-copyable values #63825

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 4 commits into from
Mar 2, 2023
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
9 changes: 9 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -807,5 +807,14 @@ ERROR(sil_movekillscopyablevalue_move_applied_to_unsupported_move, none,
"_move applied to value that the compiler does not support checking",
())

// Implicit inout-to-UnsafeRawPointer conversions
WARNING(nontrivial_to_rawpointer_conversion,none,
"forming %1 to a variable of type %0; this is likely incorrect because %2 may contain "
"an object reference.", (Type, Type, Type))

WARNING(nontrivial_string_to_rawpointer_conversion,none,
"forming %0 to an inout variable of type String exposes the internal representation "
"rather than the string contents.", (Type))

#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
1 change: 1 addition & 0 deletions include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ PROTOCOL(CaseIterable)
PROTOCOL(SIMD)
PROTOCOL(SIMDScalar)
PROTOCOL(BinaryInteger)
PROTOCOL(FixedWidthInteger)
PROTOCOL(RangeReplaceableCollection)
PROTOCOL(SerialExecutor)
PROTOCOL(GlobalActor)
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,10 @@ class alignas(1 << TypeAlignInBits) TypeBase
/// type) from `DistributedActor`.
bool isDistributedActor();

/// Determine if the type in question is an Array<T> and, if so, provide the
/// element type of the array.
Type isArrayType();

/// Determines the element type of a known
/// [Autoreleasing]Unsafe[Mutable][Raw]Pointer variant, or returns null if the
/// type is not a pointer.
Expand Down
4 changes: 0 additions & 4 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -4731,10 +4731,6 @@ class ConstraintSystem {
bool updateState = true,
bool notifyBindingInference = true);

/// Determine if the type in question is an Array<T> and, if so, provide the
/// element type of the array.
static Optional<Type> isArrayType(Type type);

/// Determine whether the given type is a dictionary and, if so, provide the
/// key and value types for the dictionary.
static Optional<std::pair<Type, Type>> isDictionaryType(Type type);
Expand Down
8 changes: 8 additions & 0 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,14 @@ CanType CanType::wrapInOptionalTypeImpl(CanType type) {
return type->wrapInOptionalType()->getCanonicalType();
}

Type TypeBase::isArrayType() {
if (auto boundStruct = getAs<BoundGenericStructType>()) {
if (boundStruct->getDecl() == getASTContext().getArrayDecl())
return boundStruct->getGenericArgs()[0];
}
return Type();
}

Type TypeBase::getAnyPointerElementType(PointerTypeKind &PTK) {
auto &C = getASTContext();
if (isUnsafeMutableRawPointer()) {
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5983,6 +5983,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
case KnownProtocolKind::SIMD:
case KnownProtocolKind::SIMDScalar:
case KnownProtocolKind::BinaryInteger:
case KnownProtocolKind::FixedWidthInteger:
case KnownProtocolKind::ObjectiveCBridgeable:
case KnownProtocolKind::DestructorSafeContainer:
case KnownProtocolKind::SwiftNewtypeWrapper:
Expand Down
58 changes: 58 additions & 0 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5701,19 +5701,74 @@ RValue RValueEmitter::visitInOutToPointerExpr(InOutToPointerExpr *E,
return RValue(SGF, E, ptr);
}

/// Implicit conversion from a nontrivial inout type to a raw pointer are
/// dangerous. For example:
///
/// func bar(_ p: UnsafeRawPointer) { ... }
/// func foo(object: inout AnyObject) {
/// bar(&object)
/// }
///
/// These conversions should be done explicitly.
///
static void diagnoseImplicitRawConversion(Type sourceTy, Type pointerTy,
SILLocation loc,
SILGenFunction &SGF) {
// Array conversion does not always go down the ArrayConverter
// path. Recognize the Array source type here both for ArrayToPointer and
// InoutToPointer cases and diagnose on the element type.
Type eltTy = sourceTy->isArrayType();
if (!eltTy)
eltTy = sourceTy;

if (SGF.getLoweredType(eltTy).isTrivial(SGF.F))
return;

auto *SM = SGF.getModule().getSwiftModule();
if (auto *fixedWidthIntegerDecl = SM->getASTContext().getProtocol(
KnownProtocolKind::FixedWidthInteger)) {
if (SM->conformsToProtocol(eltTy, fixedWidthIntegerDecl))
return;
}

PointerTypeKind kindOfPtr;
auto pointerElt = pointerTy->getAnyPointerElementType(kindOfPtr);
assert(!pointerElt.isNull() && "expected an unsafe pointer type");

// The element type may contain a reference. Disallow conversion to a "raw"
// pointer type. Consider Int8/UInt8 to be raw pointers. Trivial element types
// are filtered out above, so Int8/UInt8 pointers can't match the source
// type. But the type checker may have allowed these for direct C calls, in
// which Int8/UInt8 are equivalent to raw pointers..
if (!(pointerElt->isVoid() || pointerElt->isInt8() || pointerElt->isUInt8()))
return;

if (sourceTy->isString()) {
SGF.SGM.diagnose(loc, diag::nontrivial_string_to_rawpointer_conversion,
pointerTy);
} else {
SGF.SGM.diagnose(loc, diag::nontrivial_to_rawpointer_conversion, sourceTy,
pointerTy, eltTy);
}
}

/// Convert an l-value to a pointer type: unsafe, unsafe-mutable, or
/// autoreleasing-unsafe-mutable.
ManagedValue SILGenFunction::emitLValueToPointer(SILLocation loc, LValue &&lv,
PointerAccessInfo pointerInfo) {
assert(pointerInfo.AccessKind == lv.getAccessKind());

diagnoseImplicitRawConversion(lv.getSubstFormalType(),
pointerInfo.PointerType, loc, *this);

// The incoming lvalue should be at the abstraction level of T in
// Unsafe*Pointer<T>. Reabstract it if necessary.
auto opaqueTy = AbstractionPattern::getOpaque();
auto loweredTy = getLoweredType(opaqueTy, lv.getSubstFormalType());
if (lv.getTypeOfRValue().getASTType() != loweredTy.getASTType()) {
lv.addSubstToOrigComponent(opaqueTy, loweredTy);
}

switch (pointerInfo.PointerKind) {
case PTK_UnsafeMutablePointer:
case PTK_UnsafePointer:
Expand Down Expand Up @@ -5827,6 +5882,9 @@ SILGenFunction::emitArrayToPointer(SILLocation loc, ManagedValue array,
firstSubMap, secondSubMap, CombineSubstitutionMaps::AtIndex, 1, 0,
genericSig);

diagnoseImplicitRawConversion(accessInfo.ArrayType, accessInfo.PointerType,
loc, *this);

SmallVector<ManagedValue, 2> resultScalars;
emitApplyOfLibraryIntrinsic(loc, converter, subMap, array, SGFContext())
.getAll(resultScalars);
Expand Down
18 changes: 8 additions & 10 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6453,8 +6453,8 @@ bool ExprRewriter::peepholeCollectionUpcast(Expr *expr, Type toType,

// Array literals.
if (auto arrayLiteral = dyn_cast<ArrayExpr>(expr)) {
if (Optional<Type> elementType = ConstraintSystem::isArrayType(toType)) {
peepholeArrayUpcast(arrayLiteral, toType, bridged, *elementType, locator);
if (Type elementType = toType->isArrayType()) {
peepholeArrayUpcast(arrayLiteral, toType, bridged, elementType, locator);
return true;
}

Expand Down Expand Up @@ -6498,8 +6498,7 @@ Expr *ExprRewriter::buildCollectionUpcastExpr(
bridged, locator, 0);

// For single-parameter collections, form the upcast.
if (ConstraintSystem::isArrayType(toType) ||
ConstraintSystem::isSetType(toType)) {
if (toType->isArrayType() || ConstraintSystem::isSetType(toType)) {
return cs.cacheType(
new (ctx) CollectionUpcastConversionExpr(expr, toType, {}, conv));
}
Expand All @@ -6524,12 +6523,11 @@ Expr *ExprRewriter::buildObjCBridgeExpr(Expr *expr, Type toType,

// Bridged collection casts always succeed, so we treat them as
// collection "upcasts".
if ((ConstraintSystem::isArrayType(fromType) &&
ConstraintSystem::isArrayType(toType)) ||
(ConstraintSystem::isDictionaryType(fromType) &&
ConstraintSystem::isDictionaryType(toType)) ||
(ConstraintSystem::isSetType(fromType) &&
ConstraintSystem::isSetType(toType))) {
if ((fromType->isArrayType() && toType->isArrayType())
|| (ConstraintSystem::isDictionaryType(fromType)
&& ConstraintSystem::isDictionaryType(toType))
|| (ConstraintSystem::isSetType(fromType)
&& ConstraintSystem::isSetType(toType))) {
return buildCollectionUpcastExpr(expr, toType, /*bridged=*/true, locator);
}

Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1746,8 +1746,8 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() {
auto argType = getType(inoutExpr)->getWithoutSpecifierType();

PointerTypeKind ptr;
if (isArrayType(argType) && paramType->getAnyPointerElementType(ptr) &&
(ptr == PTK_UnsafePointer || ptr == PTK_UnsafeRawPointer)) {
if (argType->isArrayType() && paramType->getAnyPointerElementType(ptr)
&& (ptr == PTK_UnsafePointer || ptr == PTK_UnsafeRawPointer)) {
emitDiagnosticAt(inoutExpr->getLoc(),
diag::extra_address_of_unsafepointer, paramType)
.highlight(inoutExpr->getSourceRange())
Expand Down
5 changes: 0 additions & 5 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,6 @@ class FailureDiagnostic {
llvm::function_ref<void(GenericTypeParamType *, Type)> substitution =
[](GenericTypeParamType *, Type) {});

bool isArrayType(Type type) const {
auto &cs = getConstraintSystem();
return bool(cs.isArrayType(type));
}

bool conformsToKnownProtocol(Type type, KnownProtocolKind protocol) const;
};

Expand Down
6 changes: 3 additions & 3 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ TreatArrayLiteralAsDictionary *
TreatArrayLiteralAsDictionary::attempt(ConstraintSystem &cs, Type dictionaryTy,
Type arrayTy,
ConstraintLocator *locator) {
if (!cs.isArrayType(arrayTy))
if (!arrayTy->isArrayType())
return nullptr;

// Determine the ArrayExpr from the locator.
Expand Down Expand Up @@ -1676,15 +1676,15 @@ ExpandArrayIntoVarargs::attempt(ConstraintSystem &cs, Type argType,
if (!(argLoc && argLoc->getParameterFlags().isVariadic()))
return nullptr;

auto elementType = cs.isArrayType(argType);
auto elementType = argType->isArrayType();
if (!elementType)
return nullptr;

ConstraintSystem::TypeMatchOptions options;
options |= ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix;
options |= ConstraintSystem::TypeMatchFlags::TMF_GenerateConstraints;

auto result = cs.matchTypes(*elementType, paramType, ConstraintKind::Subtype,
auto result = cs.matchTypes(elementType, paramType, ConstraintKind::Subtype,
options, builder);

if (result.isFailure())
Expand Down
9 changes: 4 additions & 5 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -981,8 +981,8 @@ namespace {
isLValueBase = true;
baseObjTy = baseObjTy->getWithoutSpecifierType();
}
if (CS.isArrayType(baseObjTy.getPointer())) {

if (baseObjTy->isArrayType()) {

if (auto arraySliceTy =
dyn_cast<ArraySliceType>(baseObjTy.getPointer())) {
Expand Down Expand Up @@ -1981,14 +1981,13 @@ namespace {
};

// If a contextual type exists for this expression, apply it directly.
if (contextualType && ConstraintSystem::isArrayType(contextualType)) {
if (contextualType && contextualType->isArrayType()) {
// Now that we know we're actually going to use the type, get the
// version for use in a constraint.
contextualType = CS.getContextualType(expr, /*forConstraint=*/true);
contextualType = CS.openOpaqueType(
contextualType, contextualPurpose, locator);
Optional<Type> arrayElementType =
ConstraintSystem::isArrayType(contextualType);
Type arrayElementType = contextualType->isArrayType();
CS.addConstraint(ConstraintKind::LiteralConformsTo, contextualType,
arrayProto->getDeclaredInterfaceType(),
locator);
Expand Down
43 changes: 22 additions & 21 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5585,8 +5585,8 @@ bool ConstraintSystem::repairFailures(
// ```
if (rhs->isKnownStdlibCollectionType()) {
std::function<Type(Type)> getArrayOrSetType = [&](Type type) -> Type {
if (auto eltTy = isArrayType(type))
return getArrayOrSetType(*eltTy);
if (auto eltTy = type->isArrayType())
return getArrayOrSetType(eltTy);

if (auto eltTy = isSetType(type))
return getArrayOrSetType(*eltTy);
Expand Down Expand Up @@ -7194,7 +7194,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
// Special implicit nominal conversions.
if (!type1->is<LValueType>() && kind >= ConstraintKind::Subtype) {
// Array -> Array.
if (isArrayType(desugar1) && isArrayType(desugar2)) {
if (desugar1->isArrayType() && desugar2->isArrayType()) {
conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayUpcast);
// Dictionary -> Dictionary.
} else if (isDictionaryType(desugar1) && isDictionaryType(desugar2)) {
Expand Down Expand Up @@ -7240,8 +7240,9 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
if (!isAutoClosureArgument) {
auto inoutBaseType = inoutType1->getInOutObjectType();

auto baseIsArray = isArrayType(
getFixedTypeRecursive(inoutBaseType, /*wantRValue=*/true));
auto baseIsArray =
getFixedTypeRecursive(inoutBaseType, /*wantRValue=*/true)
->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
Expand Down Expand Up @@ -7311,7 +7312,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
if (pointerKind == PTK_UnsafePointer
|| pointerKind == PTK_UnsafeRawPointer) {
if (!isAutoClosureArgument) {
if (isArrayType(type1)) {
if (type1->isArrayType()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::ArrayToPointer);
}
Expand Down Expand Up @@ -8295,9 +8296,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo(
}

// Array<T> -> Unsafe{Raw}Pointer<T>
if (auto elt = isArrayType(resolvedTy)) {
typesToCheck.push_back(getPointerFor(PTK_UnsafePointer, *elt));
typesToCheck.push_back(getPointerFor(PTK_UnsafeRawPointer, *elt));
if (auto elt = resolvedTy->isArrayType()) {
typesToCheck.push_back(getPointerFor(PTK_UnsafePointer, elt));
typesToCheck.push_back(getPointerFor(PTK_UnsafeRawPointer, elt));
}

// inout argument -> UnsafePointer<T>, UnsafeMutablePointer<T>,
Expand Down Expand Up @@ -8327,7 +8328,7 @@ static CheckedCastKind getCheckedCastKind(ConstraintSystem *cs,
Type fromType,
Type toType) {
// Array downcasts are handled specially.
if (cs->isArrayType(fromType) && cs->isArrayType(toType)) {
if (fromType->isArrayType() && toType->isArrayType()) {
return CheckedCastKind::ArrayDowncast;
}

Expand Down Expand Up @@ -8577,9 +8578,9 @@ ConstraintSystem::simplifyCheckedCastConstraint(
auto kind = getCheckedCastKind(this, fromType, toType);
switch (kind) {
case CheckedCastKind::ArrayDowncast: {
auto fromBaseType = *isArrayType(fromType);
auto toBaseType = *isArrayType(toType);
auto fromBaseType = fromType->isArrayType();
auto toBaseType = toType->isArrayType();

auto elementLocator =
locator.withPathElement(LocatorPathElt::GenericArgument(0));
auto result = simplifyCheckedCastConstraint(fromBaseType, toBaseType,
Expand Down Expand Up @@ -11253,11 +11254,11 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1,
};

// Bridging the elements of an array.
if (auto fromElement = isArrayType(unwrappedFromType)) {
if (auto toElement = isArrayType(unwrappedToType)) {
if (auto fromElement = unwrappedFromType->isArrayType()) {
if (auto toElement = unwrappedToType->isArrayType()) {
countOptionalInjections();
auto result = simplifyBridgingConstraint(
*fromElement, *toElement, subflags,
fromElement, toElement, subflags,
locator.withPathElement(LocatorPathElt::GenericArgument(0)));
return makeCollectionResult(result);
}
Expand Down Expand Up @@ -13252,7 +13253,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(

auto t2 = type2->getDesugaredType();

auto baseType1 = getFixedTypeRecursive(*isArrayType(obj1), false);
auto baseType1 = getFixedTypeRecursive(obj1->isArrayType(), false);
auto ptr2 = getBaseTypeForPointer(t2);

increaseScore(SK_ValueToOptional, ptr2.getInt());
Expand Down Expand Up @@ -13366,8 +13367,8 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(

// T < U or T is bridged to V where V < U ===> Array<T> <c Array<U>
case ConversionRestrictionKind::ArrayUpcast: {
Type baseType1 = *isArrayType(type1);
Type baseType2 = *isArrayType(type2);
Type baseType1 = type1->isArrayType();
Type baseType2 = type2->isArrayType();

increaseScore(SK_CollectionUpcastConversion);
return matchTypes(baseType1,
Expand Down Expand Up @@ -14126,13 +14127,13 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
auto dictionaryKeyTy = DependentMemberType::get(valueBaseTy, keyAssocTy);

// Extract the array element type.
auto elemTy = isArrayType(type1);
auto elemTy = type1->isArrayType();

ConstraintLocator *elemLoc = getConstraintLocator(AE->getElement(0));
ConstraintKind kind = isDictionaryType(dictTy)
? ConstraintKind::Conversion
: ConstraintKind::Equal;
return matchTypes(*elemTy, dictionaryKeyTy, kind, subflags, elemLoc);
return matchTypes(elemTy, dictionaryKeyTy, kind, subflags, elemLoc);
}

case FixKind::ContextualMismatch:
Expand Down
Loading