Switch BooleanLiteralConvertible over to an initializer requirement.

Conforming to BooleanLiteralConvertible now requires

  init(booleanLiteral: Bool)

rather than

  static func convertFromBooleanLiteral(value: Bool) -> Self

This posed a problem for NSNumber's conformance to
BooleanLiteralConvertible. A class needs a required initializer to
satisfy an initializer requirement, but one cannot add a required
initializer via an extension. To that end, we hack the Clang importer
to import NSNumber's initWithBool with the name

  init(booleanLiteral:)

and add back the expected init(bool:) initializer in the
overlay. These tricks make NSNumber even harder to subclass, but we
don't really care: it's nearly impossible to do well anyway, and is
generally a Bad Idea.

Part of rdar://problem/18154091.

Swift SVN r21961
This commit is contained in:
Doug Gregor
2014-09-15 23:59:30 +00:00
parent e60b5f4d99
commit d93eaed9f7
13 changed files with 108 additions and 56 deletions

View File

@@ -72,9 +72,8 @@ IDENTIFIER_WITH_NAME(ConvertFromFloatLiteral, "convertFromFloatLiteral")
IDENTIFIER_WITH_NAME(ConvertFromBuiltinFloatLiteral,
"_convertFromBuiltinFloatLiteral")
IDENTIFIER(BooleanLiteralType)
IDENTIFIER_WITH_NAME(ConvertFromBooleanLiteral, "convertFromBooleanLiteral")
IDENTIFIER_WITH_NAME(ConvertFromBuiltinBooleanLiteral,
"_convertFromBuiltinBooleanLiteral")
IDENTIFIER_WITH_NAME(BuiltinBooleanLiteral, "_builtinBooleanLiteral")
IDENTIFIER_WITH_NAME(BooleanLiteral, "booleanLiteral")
IDENTIFIER(CharacterLiteralType)
IDENTIFIER_WITH_NAME(ConvertFromCharacterLiteral,

View File

@@ -427,6 +427,13 @@ ClangImporter::create(ASTContext &ctx,
importer->Impl.setObjectForKeyedSubscript
= clangContext.Selectors.getSelector(2, setObjectForKeyedSubscriptIdents);
// Create the selectors for literal conformances.
clang::IdentifierInfo *initWithBoolIdents[1] = {
&clangContext.Idents.get("initWithBool")
};
importer->Impl.initWithBool
= clangContext.Selectors.getSelector(1, initWithBoolIdents);
// Set up the imported header module.
auto *importedHeaderModule = Module::create(ctx.getIdentifier("__ObjC"), ctx);
importer->Impl.ImportedHeaderUnit =

View File

@@ -2482,6 +2482,17 @@ namespace {
return Impl.mapSelectorToDeclName(selector, /*initializer*/true);
}
/// Determine whether the given class is NSNumber or a subclass thereof.
static bool isNSNumberSubclass(const clang::ObjCInterfaceDecl *classDecl) {
if (!classDecl)
return nullptr;
if (classDecl->getName() == "NSNumber")
return true;
return isNSNumberSubclass(classDecl->getSuperClass());
}
/// \brief Given an imported method, try to import it as a constructor.
///
/// Objective-C methods in the 'init' family are imported as
@@ -2548,6 +2559,17 @@ namespace {
bool &redundant) {
redundant = false;
// Map NSNumber's initializers over to their corresponding literal-
// conversion initializers.
// FIXME: This would go away if we could implement convenience factory
// initializers in Swift.
if (objcMethod->getSelector() == Impl.initWithBool &&
isNSNumberSubclass(objcMethod->getClassInterface())) {
name = DeclName(Impl.SwiftContext, Impl.SwiftContext.Id_init,
{ Impl.SwiftContext.Id_BooleanLiteral });
required = true;
}
// Figure out the type of the container.
auto containerTy = dc->getDeclaredTypeOfContext();
assert(containerTy && "Method in non-type context?");
@@ -2637,6 +2659,10 @@ namespace {
ctor->getAttrs().isUnavailable(Impl.SwiftContext))
continue;
// Resolve the type of the constructor.
if (!ctor->hasType())
Impl.getTypeResolver()->resolveDeclSignature(ctor);
// If the types don't match, this is a different constructor with
// the same selector. This can happen when an overlay overloads an
// existing selector with a Swift-only signature.

View File

@@ -486,6 +486,9 @@ public:
/// \brief Clang's setObject:forKeyedSubscript: selector.
clang::Selector setObjectForKeyedSubscript;
/// Clang's initWithBool: selector.
clang::Selector initWithBool;
private:
Optional<Module *> checkedFoundationModule;

View File

@@ -166,7 +166,7 @@ Type Solution::computeSubstitutions(Type origType, DeclContext *dc,
/// \returns The named witness.
template <typename DeclTy>
static DeclTy *findNamedWitnessImpl(TypeChecker &tc, DeclContext *dc, Type type,
ProtocolDecl *proto, Identifier name,
ProtocolDecl *proto, DeclName name,
Diag<> diag) {
// Find the named requirement.
DeclTy *requirement = nullptr;
@@ -175,7 +175,7 @@ static DeclTy *findNamedWitnessImpl(TypeChecker &tc, DeclContext *dc, Type type,
if (!d || !d->hasName())
continue;
if (d->getName() == name) {
if (d->getFullName().matchesRef(name)) {
requirement = d;
break;
}
@@ -854,10 +854,10 @@ namespace {
Type openedType,
ProtocolDecl *protocol,
TypeOrName literalType,
Identifier literalFuncName,
DeclName literalFuncName,
ProtocolDecl *builtinProtocol,
TypeOrName builtinLiteralType,
Identifier builtinLiteralFuncName,
DeclName builtinLiteralFuncName,
bool (*isBuiltinArgType)(Type),
Diag<> brokenProtocolDiag,
Diag<> brokenBuiltinProtocolDiag);
@@ -1395,17 +1395,21 @@ namespace {
return nullptr;
auto type = simplifyType(expr->getType());
DeclName initName(tc.Context, tc.Context.Id_init,
{ tc.Context.Id_BooleanLiteral });
DeclName builtinInitName(tc.Context, tc.Context.Id_init,
{ tc.Context.Id_BuiltinBooleanLiteral });
return convertLiteral(
expr,
type,
expr->getType(),
protocol,
tc.Context.Id_BooleanLiteralType,
tc.Context.Id_ConvertFromBooleanLiteral,
initName,
builtinProtocol,
Type(BuiltinIntegerType::get(BuiltinIntegerWidth::fixed(1),
tc.Context)),
tc.Context.Id_ConvertFromBuiltinBooleanLiteral,
builtinInitName,
nullptr,
diag::boolean_literal_broken_proto,
diag::builtin_boolean_literal_broken_proto);
@@ -4187,10 +4191,10 @@ Expr *ExprRewriter::convertLiteral(Expr *literal,
Type openedType,
ProtocolDecl *protocol,
TypeOrName literalType,
Identifier literalFuncName,
DeclName literalFuncName,
ProtocolDecl *builtinProtocol,
TypeOrName builtinLiteralType,
Identifier builtinLiteralFuncName,
DeclName builtinLiteralFuncName,
bool (*isBuiltinArgType)(Type),
Diag<> brokenProtocolDiag,
Diag<> brokenBuiltinProtocolDiag) {
@@ -4948,7 +4952,7 @@ Expr *Solution::coerceToType(Expr *expr, Type toType,
Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc,
ProtocolDecl *protocol,
ProtocolConformance *conformance,
Identifier name,
DeclName name,
MutableArrayRef<Expr *> arguments,
Diag<> brokenProtocolDiag) {
// Construct an empty constraint system and solution.
@@ -4959,7 +4963,8 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc,
if (auto metaType = type->getAs<AnyMetatypeType>())
type = metaType->getInstanceType();
auto witness = findNamedWitness(*this, dc, type->getRValueType(), protocol,
auto witness = findNamedWitnessImpl<AbstractFunctionDecl>(
*this, dc, type->getRValueType(), protocol,
name, brokenProtocolDiag);
if (!witness)
return nullptr;
@@ -4975,17 +4980,25 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc,
// Form the call argument.
Expr *arg;
if (arguments.size() == 1)
if (arguments.size() == 1 && name.isSimpleName()) {
arg = arguments[0];
else {
} else {
SmallVector<TupleTypeElt, 4> elementTypes;
for (auto elt : arguments)
elementTypes.push_back(TupleTypeElt(elt->getType()));
auto names = witness->getFullName().getArgumentNames();
unsigned i = 0;
for (auto elt : arguments) {
Identifier name;
if (i < names.size())
name = names[i];
elementTypes.push_back(TupleTypeElt(elt->getType(), name));
++i;
}
arg = TupleExpr::create(Context,
base->getStartLoc(),
arguments,
witness->getFullName().getArgumentNames(),
names,
{ },
base->getEndLoc(),
/*hasTrailingClosure=*/false,

View File

@@ -57,31 +57,37 @@ static Expr *getBooleanLiteral(ASTContext &C, bool value) {
// need to rely on it already having been type-checked. This only
// matters when building the standard library.
auto boolDecl = C.getBoolDecl();
auto convertFunc
= boolDecl->lookupDirect(C.Id_ConvertFromBuiltinBooleanLiteral)[0];
auto convertFuncAppliedTy = FunctionType::get(ParenType::get(C, i1Ty),
DeclName initName(C, C.Id_init, { C.Id_BuiltinBooleanLiteral });
auto convertInit = boolDecl->lookupDirect(initName)[0];
auto convertFuncParamTy = TupleType::get(
{TupleTypeElt(i1Ty, C.Id_BuiltinBooleanLiteral)},
C);
auto convertFuncAppliedTy = FunctionType::get(convertFuncParamTy,
boolDecl->getDeclaredType());
auto convertFuncTy = FunctionType::get(boolDecl->getType(),
convertFuncAppliedTy);
assert(!convertFunc->hasType() ||
convertFunc->getType()->isEqual(convertFuncTy) &&
"_convertFromBuiltinBooleanLiteral has unexpected type");
auto convertRef = new (C) DeclRefExpr(convertFunc, SourceLoc(),
assert(!convertInit->hasType() ||
convertInit->getType()->isEqual(convertFuncTy) &&
"init(_builtinBooleanLiteral:) has unexpected type");
auto initRef = new (C) DeclRefExpr(convertInit, SourceLoc(),
/*implicit*/ true,
/*direct access*/false,
convertFuncTy);
// Form Bool._convertFromBuiltinBooleanLiteral
// Form Bool.init(_builtinBooleanLiteral:).
auto boolRef = TypeExpr::createImplicit(boolDecl->getDeclaredType(), C);
Expr *call = new (C) DotSyntaxCallExpr(convertRef, SourceLoc(), boolRef);
Expr *call = new (C) ConstructorRefCallExpr(initRef, boolRef,
convertFuncAppliedTy);
call->setImplicit(true);
call->setType(convertFuncAppliedTy);
// Call Bool._convertFromBuiltinBooleanLiteral.
Expr *arg = new (C) ParenExpr(SourceLoc(), lit, SourceLoc(),
/*has trailing closure*/ false,
lit->getType());
arg->setImplicit();
// Call Bool.init(_builtinBooleanLiteral:).
Expr *arg = TupleExpr::create(
C, SourceLoc(), lit,
{ C.Id_BuiltinBooleanLiteral }, { SourceLoc() },
SourceLoc(), /*hasTrailingClosure=*/false,
/*Implicit=*/true, convertFuncParamTy);
call = new (C) CallExpr(call, arg, /*implicit*/ true,
boolDecl->getDeclaredType());
return call;

View File

@@ -929,7 +929,7 @@ public:
Expr *callWitness(Expr *base, DeclContext *dc,
ProtocolDecl *protocol,
ProtocolConformance *conformance,
Identifier name,
DeclName name,
MutableArrayRef<Expr *> arguments,
Diag<> brokenProtocolDiag);

View File

@@ -26,14 +26,13 @@ public struct Bool {
extension Bool : _BuiltinBooleanLiteralConvertible, BooleanLiteralConvertible {
@transparent
public static func _convertFromBuiltinBooleanLiteral(value: Builtin.Int1)
-> Bool {
return Bool(value)
public init(_builtinBooleanLiteral value: Builtin.Int1) {
self.value = value
}
@transparent
public static func convertFromBooleanLiteral(value: Bool) -> Bool {
return value
public init(booleanLiteral value: Bool) {
self = value
}
}

View File

@@ -228,13 +228,12 @@ public protocol FloatLiteralConvertible {
}
public protocol _BuiltinBooleanLiteralConvertible {
class func _convertFromBuiltinBooleanLiteral(
value: Builtin.Int1) -> Self
init(_builtinBooleanLiteral value: Builtin.Int1)
}
public protocol BooleanLiteralConvertible {
typealias BooleanLiteralType : _BuiltinBooleanLiteralConvertible
class func convertFromBooleanLiteral(value: BooleanLiteralType) -> Self
init(booleanLiteral value: BooleanLiteralType)
}
public protocol _BuiltinCharacterLiteralConvertible {

View File

@@ -585,8 +585,8 @@ extension NSNumber : FloatLiteralConvertible, IntegerLiteralConvertible,
return self(double: value)
}
public class func convertFromBooleanLiteral(value: Bool) -> Self {
return self(bool: value)
public convenience init(bool value: Bool) {
self.init(booleanLiteral: value)
}
}

View File

@@ -54,8 +54,8 @@ public struct ObjCBool : BooleanType, BooleanLiteralConvertible {
}
@transparent
public static func convertFromBooleanLiteral(value: Bool) -> ObjCBool {
return ObjCBool(value)
public init(booleanLiteral value: Bool) {
self.init(value)
}
}

View File

@@ -17,14 +17,14 @@ func matchesEither(#input: Hive, #a: Hive, #b: Hive) -> Bool {
// CHECK: br [[RET_TRUE]]
case a, b:
// CHECK: [[RET_TRUE]]:
// CHECK: function_ref @_TFSb33_convertFromBuiltinBooleanLiteral
// CHECK: function_ref @_TFSbCfMSbFT22_builtinBooleanLiteralBi1__Sb
return true
// CHECK: [[NOT_CASE2]]:
// CHECK: br [[RET_FALSE:bb[0-9]+]]
default:
// CHECK: [[RET_FALSE]]:
// CHECK: function_ref @_TFSb33_convertFromBuiltinBooleanLiteral
// CHECK: function_ref @_TFSbCfMSbFT22_builtinBooleanLiteralBi1__Sb
return false
}
}

View File

@@ -13,8 +13,8 @@ func defaultBoolLiterals() {
struct CustomBool : BooleanLiteralConvertible {
let value: Bool
static func convertFromBooleanLiteral(value: Bool) -> CustomBool {
return CustomBool(value: value)
init(booleanLiteral value: Bool) {
self.value = value
}
}