mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Sema: Allow string-to-pointer argument conversions.
Allow a String value to be implicitly converted to ConstUnsafePointer<{UInt8,Int8,Void}> by string-to-pointer conversion, when enabled by a staging flag.
Swift SVN r19366
This commit is contained in:
@@ -82,6 +82,9 @@ namespace swift {
|
||||
/// Should access control be respected?
|
||||
bool EnableAccessControl = false;
|
||||
|
||||
/// Enable string-to-pointer argument conversions.
|
||||
bool EnableStringPointerConversion = false;
|
||||
|
||||
/// Implicit target configuration options. There are currently two
|
||||
/// supported target configuration values:
|
||||
/// os - The active os target (OSX or IOS)
|
||||
|
||||
@@ -128,6 +128,10 @@ def enable_dynamic_value_type_layout :
|
||||
def enable_experimental_patterns : Flag<["-"], "enable-experimental-patterns">,
|
||||
HelpText<"Enable experimental 'switch' pattern matching features">;
|
||||
|
||||
def enable_string_pointer_conversion
|
||||
: Flag<["-"], "enable-string-pointer-conversion">,
|
||||
HelpText<"Enable string-to-pointer argument conversion">;
|
||||
|
||||
def enable_character_literals : Flag<["-"], "enable-character-literals">,
|
||||
HelpText<"Enable legacy character literals">;
|
||||
|
||||
|
||||
@@ -536,6 +536,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
|
||||
Opts.EnableExperimentalPatterns |= Args.hasArg(OPT_enable_experimental_patterns);
|
||||
|
||||
Opts.EnableStringPointerConversion |= Args.hasArg(OPT_enable_string_pointer_conversion);
|
||||
|
||||
Opts.EnableCharacterLiterals |= Args.hasArg(OPT_enable_character_literals);
|
||||
|
||||
if (auto A = Args.getLastArg(OPT_enable_access_control,
|
||||
|
||||
@@ -3975,6 +3975,11 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
|
||||
return new (tc.Context) ArrayToPointerExpr(expr, toType);
|
||||
}
|
||||
|
||||
case ConversionRestrictionKind::StringToPointer: {
|
||||
tc.requirePointerArgumentIntrinsics(expr->getLoc());
|
||||
return new (tc.Context) StringToPointerExpr(expr, toType);
|
||||
}
|
||||
|
||||
case ConversionRestrictionKind::PointerToPointer: {
|
||||
tc.requirePointerArgumentIntrinsics(expr->getLoc());
|
||||
return new (tc.Context) PointerToPointerExpr(expr, toType);
|
||||
|
||||
@@ -1294,6 +1294,20 @@ tryUserConversion(ConstraintSystem &cs, Type type, ConstraintKind kind,
|
||||
return ConstraintSystem::SolutionKind::Solved;
|
||||
}
|
||||
|
||||
static bool isStringCompatiblePointerBaseType(TypeChecker &TC,
|
||||
DeclContext *DC,
|
||||
Type baseType) {
|
||||
// Allow strings to be passed to pointer-to-byte or pointer-to-void types.
|
||||
if (baseType->isEqual(TC.getInt8Type(DC)))
|
||||
return true;
|
||||
if (baseType->isEqual(TC.getUInt8Type(DC)))
|
||||
return true;
|
||||
if (baseType->isEqual(TC.Context.TheEmptyTupleType))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ConstraintSystem::SolutionKind
|
||||
ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
|
||||
unsigned flags,
|
||||
@@ -1766,7 +1780,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
|
||||
|
||||
// We can potentially convert from an UnsafePointer of a different
|
||||
// type, if we're a void pointer.
|
||||
if (bgt1 && bgt1->getDecl() == getASTContext().getUnsafePointerDecl()) {
|
||||
if (bgt1 && bgt1->getDecl()
|
||||
== getASTContext().getUnsafePointerDecl()) {
|
||||
// Favor an UnsafePointer-to-UnsafePointer conversion.
|
||||
if (bgt1->getDecl() != bgt2->getDecl())
|
||||
increaseScore(ScoreKind::SK_ScalarPointerConversion);
|
||||
@@ -1774,13 +1789,27 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
|
||||
ConversionRestrictionKind::PointerToPointer);
|
||||
}
|
||||
|
||||
// ConstUnsafePointer can also be converted from an array value, or
|
||||
// a ConstUnsafePointer or AutoreleasingUnsafePointer.
|
||||
// ConstUnsafePointer can also be converted from an array or string
|
||||
// value, or a ConstUnsafePointer or AutoreleasingUnsafePointer.
|
||||
if (bgt2->getDecl() == getASTContext().getConstUnsafePointerDecl()){
|
||||
if (isArrayType(type1)) {
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::ArrayToPointer);
|
||||
}
|
||||
|
||||
// The pointer can be converted from a string, if the element type
|
||||
// is compatible.
|
||||
if (TC.Context.LangOpts.EnableStringPointerConversion &&
|
||||
type1->isEqual(TC.getStringType(DC))) {
|
||||
TypeVariableType *tv = nullptr;
|
||||
auto baseTy = getFixedTypeRecursive(bgt2->getGenericArgs()[0],
|
||||
tv, false, false);
|
||||
|
||||
if (tv || isStringCompatiblePointerBaseType(TC, DC, baseTy))
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::StringToPointer);
|
||||
}
|
||||
|
||||
if (bgt1 && bgt1->getDecl()
|
||||
== getASTContext().getConstUnsafePointerDecl()) {
|
||||
conversionsOrFixes.push_back(
|
||||
@@ -1794,7 +1823,6 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Strings
|
||||
} else if (bgt2->getDecl()
|
||||
== getASTContext().getAutoreleasingUnsafePointerDecl()) {
|
||||
// AutoUnsafePointer can be converted from an inout reference to a
|
||||
@@ -3169,7 +3197,8 @@ static Type getBaseTypeForPointer(ConstraintSystem &cs, TypeBase *type) {
|
||||
auto bgt = type->castTo<BoundGenericType>();
|
||||
assert((bgt->getDecl() == cs.getASTContext().getUnsafePointerDecl()
|
||||
|| bgt->getDecl() == cs.getASTContext().getConstUnsafePointerDecl()
|
||||
|| bgt->getDecl() == cs.getASTContext().getAutoreleasingUnsafePointerDecl())
|
||||
|| bgt->getDecl()
|
||||
== cs.getASTContext().getAutoreleasingUnsafePointerDecl())
|
||||
&& "conversion is not to a pointer type");
|
||||
return bgt->getGenericArgs()[0];
|
||||
}
|
||||
@@ -3325,6 +3354,52 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
|
||||
subFlags, locator);
|
||||
}
|
||||
|
||||
// String ===> ConstUnsafePointer<[U]Int8>
|
||||
case ConversionRestrictionKind::StringToPointer: {
|
||||
addContextualScore();
|
||||
|
||||
auto baseType2 = getBaseTypeForPointer(*this, type2->getDesugaredType());
|
||||
|
||||
// The pointer element type must be void or a byte-sized type.
|
||||
// TODO: Handle different encodings based on pointer element type, such as
|
||||
// UTF16 for [U]Int16 or UTF32 for [U]Int32. For now we only interop with
|
||||
// Int8 pointers using UTF8 encoding.
|
||||
TypeVariableType *btv2 = nullptr;
|
||||
baseType2 = getFixedTypeRecursive(baseType2, btv2, false, false);
|
||||
// If we haven't resolved the element type, generate constraints.
|
||||
if (btv2) {
|
||||
if (flags & TMF_GenerateConstraints) {
|
||||
auto int8Con = Constraint::create(*this, ConstraintKind::Bind,
|
||||
btv2, TC.getInt8Type(DC),
|
||||
DeclName(),
|
||||
getConstraintLocator(locator));
|
||||
auto uint8Con = Constraint::create(*this, ConstraintKind::Bind,
|
||||
btv2, TC.getUInt8Type(DC),
|
||||
DeclName(),
|
||||
getConstraintLocator(locator));
|
||||
auto voidCon = Constraint::create(*this, ConstraintKind::Bind,
|
||||
btv2, TC.Context.TheEmptyTupleType,
|
||||
DeclName(),
|
||||
getConstraintLocator(locator));
|
||||
|
||||
Constraint *disjunctionChoices[] = {int8Con, uint8Con, voidCon};
|
||||
auto disjunction = Constraint::createDisjunction(*this,
|
||||
disjunctionChoices,
|
||||
getConstraintLocator(locator));
|
||||
|
||||
addConstraint(disjunction);
|
||||
return SolutionKind::Solved;
|
||||
}
|
||||
|
||||
return SolutionKind::Unsolved;
|
||||
}
|
||||
|
||||
if (!isStringCompatiblePointerBaseType(TC, DC, baseType2)) {
|
||||
return SolutionKind::Unsolved;
|
||||
}
|
||||
return SolutionKind::Solved;
|
||||
}
|
||||
|
||||
// T <p U ===> inout T <a UnsafePointer<U>
|
||||
case ConversionRestrictionKind::InoutToPointer: {
|
||||
addContextualScore();
|
||||
|
||||
@@ -308,6 +308,8 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) {
|
||||
return "[protocol-metatype-to-object]";
|
||||
case ConversionRestrictionKind::ArrayToPointer:
|
||||
return "[array-to-pointer]";
|
||||
case ConversionRestrictionKind::StringToPointer:
|
||||
return "[string-to-pointer]";
|
||||
case ConversionRestrictionKind::InoutToPointer:
|
||||
return "[inout-to-pointer]";
|
||||
case ConversionRestrictionKind::PointerToPointer:
|
||||
|
||||
@@ -163,6 +163,8 @@ enum class ConversionRestrictionKind {
|
||||
InoutToPointer,
|
||||
/// Array-to-pointer conversion.
|
||||
ArrayToPointer,
|
||||
/// String-to-pointer conversion.
|
||||
StringToPointer,
|
||||
/// Pointer-to-pointer conversion.
|
||||
PointerToPointer,
|
||||
/// Lvalue-to-rvalue conversion.
|
||||
|
||||
@@ -90,6 +90,28 @@ Type TypeChecker::getNSStringType(DeclContext *dc) {
|
||||
return NSStringType;
|
||||
}
|
||||
|
||||
static Type getStdlibType(TypeChecker &TC, Type &cached, DeclContext *dc,
|
||||
StringRef name) {
|
||||
if (cached.isNull()) {
|
||||
Module *stdlib = TC.Context.getStdlibModule();
|
||||
LookupTypeResult lookup = TC.lookupMemberType(ModuleType::get(stdlib),
|
||||
TC.Context.getIdentifier(name), dc);
|
||||
if (lookup)
|
||||
cached = lookup.back().second;
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
Type TypeChecker::getStringType(DeclContext *dc) {
|
||||
return ::getStdlibType(*this, StringType, dc, "String");
|
||||
}
|
||||
Type TypeChecker::getInt8Type(DeclContext *dc) {
|
||||
return ::getStdlibType(*this, Int8Type, dc, "Int8");
|
||||
}
|
||||
Type TypeChecker::getUInt8Type(DeclContext *dc) {
|
||||
return ::getStdlibType(*this, UInt8Type, dc, "UInt8");
|
||||
}
|
||||
|
||||
Type
|
||||
TypeChecker::getDynamicBridgedThroughObjCClass(DeclContext *dc,
|
||||
Type dynamicType,
|
||||
|
||||
@@ -272,6 +272,9 @@ private:
|
||||
Type ArrayLiteralType;
|
||||
Type DictionaryLiteralType;
|
||||
Type NSStringType;
|
||||
Type StringType;
|
||||
Type Int8Type;
|
||||
Type UInt8Type;
|
||||
|
||||
/// The \c Swift.UnsafePointer<T> declaration.
|
||||
Optional<NominalTypeDecl *> ArrayDecl;
|
||||
@@ -313,6 +316,9 @@ public:
|
||||
Type getOptionalType(SourceLoc loc, Type elementType);
|
||||
Type getImplicitlyUnwrappedOptionalType(SourceLoc loc, Type elementType);
|
||||
Type getNSStringType(DeclContext *dc);
|
||||
Type getStringType(DeclContext *dc);
|
||||
Type getInt8Type(DeclContext *dc);
|
||||
Type getUInt8Type(DeclContext *dc);
|
||||
|
||||
Expr *buildArrayInjectionFnRef(DeclContext *dc,
|
||||
ArraySliceType *sliceType,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// RUN: %swift -parse -verify %s
|
||||
// RUN: %swift -enable-string-pointer-conversion -parse -verify %s
|
||||
|
||||
class C {}
|
||||
class D {}
|
||||
|
||||
func takesMutablePointer(x: UnsafePointer<Int>) {} // expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}
|
||||
func takesMutableVoidPointer(x: UnsafePointer<Void>) {} // expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}
|
||||
func takesMutablePointer(x: UnsafePointer<Int>) {} // expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}
|
||||
func takesMutableVoidPointer(x: UnsafePointer<Void>) {} // expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}
|
||||
func takesMutableInt8Pointer(x: UnsafePointer<Int8>) {} // expected-note{{}}expected-note{{}}
|
||||
func takesMutableArrayPointer(x: UnsafePointer<[Int]>) {} // expected-note{{}}
|
||||
func takesConstPointer(x: ConstUnsafePointer<Int>) -> Character { return "x" } // expected-note{{}}expected-note{{}}
|
||||
func takesConstPointer(x: ConstUnsafePointer<Int>) -> Character { return "x" } // expected-note{{}}expected-note{{}}expected-note{{}}
|
||||
func takesConstInt8Pointer(x: ConstUnsafePointer<Int8>) {}
|
||||
func takesConstUInt8Pointer(x: ConstUnsafePointer<UInt8>) {}
|
||||
func takesConstVoidPointer(x: ConstUnsafePointer<Void>) {}
|
||||
func takesAutoreleasingPointer(x: AutoreleasingUnsafePointer<C>) {} // expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}
|
||||
|
||||
@@ -134,6 +137,19 @@ func constVoidPointerArguments(p: UnsafePointer<Int>,
|
||||
x = afp // expected-error{{}}
|
||||
}
|
||||
|
||||
func stringArguments(var s: String) {
|
||||
takesConstVoidPointer(s)
|
||||
takesConstInt8Pointer(s)
|
||||
takesConstUInt8Pointer(s)
|
||||
takesConstPointer(s) // expected-error{{}}
|
||||
|
||||
takesMutableVoidPointer(s) // expected-error{{}}
|
||||
takesMutableInt8Pointer(s) // expected-error{{}}
|
||||
takesMutableInt8Pointer(&s) // expected-error{{}}
|
||||
takesMutablePointer(s) // expected-error{{}}
|
||||
takesMutablePointer(&s) // expected-error{{}}
|
||||
}
|
||||
|
||||
func autoreleasingPointerArguments(p: UnsafePointer<Int>,
|
||||
cp: ConstUnsafePointer<Int>,
|
||||
ap: AutoreleasingUnsafePointer<C>) {
|
||||
|
||||
Reference in New Issue
Block a user