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:
Joe Groff
2014-06-30 20:46:04 +00:00
parent b97490b139
commit a796c76ae4
10 changed files with 153 additions and 16 deletions

View File

@@ -81,6 +81,9 @@ namespace swift {
/// Should access control be respected? /// Should access control be respected?
bool EnableAccessControl = false; bool EnableAccessControl = false;
/// Enable string-to-pointer argument conversions.
bool EnableStringPointerConversion = false;
/// Implicit target configuration options. There are currently two /// Implicit target configuration options. There are currently two
/// supported target configuration values: /// supported target configuration values:

View File

@@ -128,6 +128,10 @@ def enable_dynamic_value_type_layout :
def enable_experimental_patterns : Flag<["-"], "enable-experimental-patterns">, def enable_experimental_patterns : Flag<["-"], "enable-experimental-patterns">,
HelpText<"Enable experimental 'switch' pattern matching features">; 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">, def enable_character_literals : Flag<["-"], "enable-character-literals">,
HelpText<"Enable legacy character literals">; HelpText<"Enable legacy character literals">;

View File

@@ -536,6 +536,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.EnableExperimentalPatterns |= Args.hasArg(OPT_enable_experimental_patterns); 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); Opts.EnableCharacterLiterals |= Args.hasArg(OPT_enable_character_literals);
if (auto A = Args.getLastArg(OPT_enable_access_control, if (auto A = Args.getLastArg(OPT_enable_access_control,

View File

@@ -3975,6 +3975,11 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
return new (tc.Context) ArrayToPointerExpr(expr, 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: { case ConversionRestrictionKind::PointerToPointer: {
tc.requirePointerArgumentIntrinsics(expr->getLoc()); tc.requirePointerArgumentIntrinsics(expr->getLoc());
return new (tc.Context) PointerToPointerExpr(expr, toType); return new (tc.Context) PointerToPointerExpr(expr, toType);

View File

@@ -1294,6 +1294,20 @@ tryUserConversion(ConstraintSystem &cs, Type type, ConstraintKind kind,
return ConstraintSystem::SolutionKind::Solved; 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::SolutionKind
ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind, ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
unsigned flags, unsigned flags,
@@ -1766,35 +1780,49 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
// We can potentially convert from an UnsafePointer of a different // We can potentially convert from an UnsafePointer of a different
// type, if we're a void pointer. // 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. // Favor an UnsafePointer-to-UnsafePointer conversion.
if (bgt1->getDecl() != bgt2->getDecl()) if (bgt1->getDecl() != bgt2->getDecl())
increaseScore(ScoreKind::SK_ScalarPointerConversion); increaseScore(ScoreKind::SK_ScalarPointerConversion);
conversionsOrFixes.push_back( conversionsOrFixes.push_back(
ConversionRestrictionKind::PointerToPointer); ConversionRestrictionKind::PointerToPointer);
} }
// ConstUnsafePointer can also be converted from an array value, or // ConstUnsafePointer can also be converted from an array or string
// a ConstUnsafePointer or AutoreleasingUnsafePointer. // value, or a ConstUnsafePointer or AutoreleasingUnsafePointer.
if (bgt2->getDecl() == getASTContext().getConstUnsafePointerDecl()) { if (bgt2->getDecl() == getASTContext().getConstUnsafePointerDecl()){
if (isArrayType(type1)) { if (isArrayType(type1)) {
conversionsOrFixes.push_back( conversionsOrFixes.push_back(
ConversionRestrictionKind::ArrayToPointer); 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() if (bgt1 && bgt1->getDecl()
== getASTContext().getConstUnsafePointerDecl()) { == getASTContext().getConstUnsafePointerDecl()) {
conversionsOrFixes.push_back( conversionsOrFixes.push_back(
ConversionRestrictionKind::PointerToPointer); ConversionRestrictionKind::PointerToPointer);
} }
if (bgt1 if (bgt1
&& bgt1->getDecl() && bgt1->getDecl()
== getASTContext().getAutoreleasingUnsafePointerDecl()) { == getASTContext().getAutoreleasingUnsafePointerDecl()) {
conversionsOrFixes.push_back( conversionsOrFixes.push_back(
ConversionRestrictionKind::PointerToPointer); ConversionRestrictionKind::PointerToPointer);
} }
} }
} }
// TODO: Strings
} else if (bgt2->getDecl() } else if (bgt2->getDecl()
== getASTContext().getAutoreleasingUnsafePointerDecl()) { == getASTContext().getAutoreleasingUnsafePointerDecl()) {
// AutoUnsafePointer can be converted from an inout reference to a // 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>(); auto bgt = type->castTo<BoundGenericType>();
assert((bgt->getDecl() == cs.getASTContext().getUnsafePointerDecl() assert((bgt->getDecl() == cs.getASTContext().getUnsafePointerDecl()
|| bgt->getDecl() == cs.getASTContext().getConstUnsafePointerDecl() || bgt->getDecl() == cs.getASTContext().getConstUnsafePointerDecl()
|| bgt->getDecl() == cs.getASTContext().getAutoreleasingUnsafePointerDecl()) || bgt->getDecl()
== cs.getASTContext().getAutoreleasingUnsafePointerDecl())
&& "conversion is not to a pointer type"); && "conversion is not to a pointer type");
return bgt->getGenericArgs()[0]; return bgt->getGenericArgs()[0];
} }
@@ -3325,6 +3354,52 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
subFlags, locator); 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> // T <p U ===> inout T <a UnsafePointer<U>
case ConversionRestrictionKind::InoutToPointer: { case ConversionRestrictionKind::InoutToPointer: {
addContextualScore(); addContextualScore();

View File

@@ -308,6 +308,8 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) {
return "[protocol-metatype-to-object]"; return "[protocol-metatype-to-object]";
case ConversionRestrictionKind::ArrayToPointer: case ConversionRestrictionKind::ArrayToPointer:
return "[array-to-pointer]"; return "[array-to-pointer]";
case ConversionRestrictionKind::StringToPointer:
return "[string-to-pointer]";
case ConversionRestrictionKind::InoutToPointer: case ConversionRestrictionKind::InoutToPointer:
return "[inout-to-pointer]"; return "[inout-to-pointer]";
case ConversionRestrictionKind::PointerToPointer: case ConversionRestrictionKind::PointerToPointer:

View File

@@ -163,6 +163,8 @@ enum class ConversionRestrictionKind {
InoutToPointer, InoutToPointer,
/// Array-to-pointer conversion. /// Array-to-pointer conversion.
ArrayToPointer, ArrayToPointer,
/// String-to-pointer conversion.
StringToPointer,
/// Pointer-to-pointer conversion. /// Pointer-to-pointer conversion.
PointerToPointer, PointerToPointer,
/// Lvalue-to-rvalue conversion. /// Lvalue-to-rvalue conversion.

View File

@@ -90,6 +90,28 @@ Type TypeChecker::getNSStringType(DeclContext *dc) {
return NSStringType; 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 Type
TypeChecker::getDynamicBridgedThroughObjCClass(DeclContext *dc, TypeChecker::getDynamicBridgedThroughObjCClass(DeclContext *dc,
Type dynamicType, Type dynamicType,

View File

@@ -272,6 +272,9 @@ private:
Type ArrayLiteralType; Type ArrayLiteralType;
Type DictionaryLiteralType; Type DictionaryLiteralType;
Type NSStringType; Type NSStringType;
Type StringType;
Type Int8Type;
Type UInt8Type;
/// The \c Swift.UnsafePointer<T> declaration. /// The \c Swift.UnsafePointer<T> declaration.
Optional<NominalTypeDecl *> ArrayDecl; Optional<NominalTypeDecl *> ArrayDecl;
@@ -313,6 +316,9 @@ public:
Type getOptionalType(SourceLoc loc, Type elementType); Type getOptionalType(SourceLoc loc, Type elementType);
Type getImplicitlyUnwrappedOptionalType(SourceLoc loc, Type elementType); Type getImplicitlyUnwrappedOptionalType(SourceLoc loc, Type elementType);
Type getNSStringType(DeclContext *dc); Type getNSStringType(DeclContext *dc);
Type getStringType(DeclContext *dc);
Type getInt8Type(DeclContext *dc);
Type getUInt8Type(DeclContext *dc);
Expr *buildArrayInjectionFnRef(DeclContext *dc, Expr *buildArrayInjectionFnRef(DeclContext *dc,
ArraySliceType *sliceType, ArraySliceType *sliceType,

View File

@@ -1,12 +1,15 @@
// RUN: %swift -parse -verify %s // RUN: %swift -enable-string-pointer-conversion -parse -verify %s
class C {} class C {}
class D {} class D {}
func takesMutablePointer(x: UnsafePointer<Int>) {} // expected-note{{}}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{{}} 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 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 takesConstVoidPointer(x: ConstUnsafePointer<Void>) {}
func takesAutoreleasingPointer(x: AutoreleasingUnsafePointer<C>) {} // expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}}expected-note{{}} 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{{}} 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>, func autoreleasingPointerArguments(p: UnsafePointer<Int>,
cp: ConstUnsafePointer<Int>, cp: ConstUnsafePointer<Int>,
ap: AutoreleasingUnsafePointer<C>) { ap: AutoreleasingUnsafePointer<C>) {