Skip to content

Revert " Remove extensions on ImplicitlyUnwrappedOptional from the stdlib." #13967

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 3 commits into from
Jan 17, 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
104 changes: 21 additions & 83 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,11 @@ namespace {
};
} // end anonymous namespace

static std::tuple<Type, Type, OptionalAdjustmentKind>
getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
Type witnessType, bool witnessTypeIsIUO,
VarianceKind variance) {
static std::tuple<Type,Type, OptionalAdjustmentKind>
getTypesToCompare(ValueDecl *reqt,
Type reqtType,
Type witnessType,
VarianceKind variance) {
// For @objc protocols, deal with differences in the optionality.
// FIXME: It probably makes sense to extend this to non-@objc
// protocols as well, but this requires more testing.
Expand All @@ -159,11 +160,6 @@ getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
break;

case OTK_Optional:
if (witnessTypeIsIUO) {
optAdjustment = OptionalAdjustmentKind::RemoveIUO;
break;
}

switch (variance) {
case VarianceKind::None:
case VarianceKind::Covariant:
Expand All @@ -183,17 +179,6 @@ getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
break;

case OTK_Optional:
// When the requirement is an IUO, all is permitted, because we
// assume that the user knows more about the signature than we
// have information in the protocol.
if (reqtTypeIsIUO)
break;

if (witnessTypeIsIUO) {
optAdjustment = OptionalAdjustmentKind::IUOToOptional;
break;
}

switch (witnessOptKind) {
case OTK_None:
switch (variance) {
Expand Down Expand Up @@ -224,15 +209,6 @@ getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
// have information in the protocol.
break;
}
} else {
// FIXME: Until IUOs are removed from the type system, make
// sure we turn these both into optionals for the purpose of
// comparing types since we could be producing IUOs in some
// places and optionals in others.
if (auto objTy = reqtType->getImplicitlyUnwrappedOptionalObjectType())
reqtType = OptionalType::get(objTy);
if (auto objTy = witnessType->getImplicitlyUnwrappedOptionalObjectType())
witnessType = OptionalType::get(objTy);
}

return std::make_tuple(reqtType, witnessType, optAdjustment);
Expand Down Expand Up @@ -367,14 +343,6 @@ static bool checkObjCWitnessSelector(TypeChecker &tc, ValueDecl *req,
return false;
}

static ParameterList *getParameterList(ValueDecl *value) {
if (auto func = dyn_cast<AbstractFunctionDecl>(value))
return func->getParameterList(func->getDeclContext()->isTypeContext());

auto subscript = cast<SubscriptDecl>(value);
return subscript->getIndices();
}

RequirementMatch
swift::matchWitness(
TypeChecker &tc,
Expand Down Expand Up @@ -522,24 +490,18 @@ swift::matchWitness(
// Result types must match.
// FIXME: Could allow (trivial?) subtyping here.
if (!ignoreReturnType) {
auto reqTypeIsIUO =
req->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
auto witnessTypeIsIUO =
witness->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
auto types =
getTypesToCompare(req, reqResultType, reqTypeIsIUO, witnessResultType,
witnessTypeIsIUO, VarianceKind::Covariant);
auto types = getTypesToCompare(req, reqResultType,
witnessResultType,
VarianceKind::Covariant);

// Record optional adjustment, if any.
if (std::get<2>(types) != OptionalAdjustmentKind::None) {
optionalAdjustments.push_back(
OptionalAdjustment(std::get<2>(types)));
}

if (!req->isObjC() && reqTypeIsIUO != witnessTypeIsIUO)
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);

if (auto result = matchTypes(std::get<0>(types), std::get<1>(types))) {
if (auto result = matchTypes(std::get<0>(types),
std::get<1>(types))) {
return std::move(result.getValue());
}
}
Expand All @@ -555,12 +517,6 @@ swift::matchWitness(
return RequirementMatch(witness, MatchKind::TypeConflict,
witnessType);

ParameterList *witnessParamList = getParameterList(witness);
assert(witnessParamList->size() == witnessParams.size());

ParameterList *reqParamList = getParameterList(req);
assert(reqParamList->size() == reqParams.size());

// Match each of the parameters.
for (unsigned i = 0, n = reqParams.size(); i != n; ++i) {
// Variadic bits must match.
Expand All @@ -574,33 +530,21 @@ swift::matchWitness(
if (reqParams[i].isInOut() != witnessParams[i].isInOut())
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);

auto reqParamDecl = reqParamList->get(i);
auto witnessParamDecl = witnessParamList->get(i);

auto reqParamTypeIsIUO =
reqParamDecl->getAttrs()
.hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
auto witnessParamTypeIsIUO =
witnessParamDecl->getAttrs()
.hasAttribute<ImplicitlyUnwrappedOptionalAttr>();

// Gross hack: strip a level of unchecked-optionality off both
// sides when matching against a protocol imported from Objective-C.
auto types =
getTypesToCompare(req, reqParams[i].getType(), reqParamTypeIsIUO,
witnessParams[i].getType(), witnessParamTypeIsIUO,
VarianceKind::Contravariant);
auto types = getTypesToCompare(req, reqParams[i].getType(),
witnessParams[i].getType(),
VarianceKind::Contravariant);

// Record any optional adjustment that occurred.
if (std::get<2>(types) != OptionalAdjustmentKind::None) {
optionalAdjustments.push_back(
OptionalAdjustment(std::get<2>(types), i));
}

if (!req->isObjC() && reqParamTypeIsIUO != witnessParamTypeIsIUO)
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);

if (auto result = matchTypes(std::get<0>(types), std::get<1>(types))) {
// Check whether the parameter types match.
if (auto result = matchTypes(std::get<0>(types),
std::get<1>(types))) {
return std::move(result.getValue());
}
}
Expand All @@ -612,22 +556,16 @@ swift::matchWitness(
}

} else {
auto reqTypeIsIUO =
req->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
auto witnessTypeIsIUO =
witness->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
auto types = getTypesToCompare(req, reqType, reqTypeIsIUO, witnessType,
witnessTypeIsIUO, VarianceKind::None);
// Simple case: add the constraint.
auto types = getTypesToCompare(req, reqType, witnessType,
VarianceKind::None);

// Record optional adjustment, if any.
if (std::get<2>(types) != OptionalAdjustmentKind::None) {
optionalAdjustments.push_back(
OptionalAdjustment(std::get<2>(types)));
}

if (!req->isObjC() && reqTypeIsIUO != witnessTypeIsIUO)
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);

if (auto result = matchTypes(std::get<0>(types), std::get<1>(types))) {
return std::move(result.getValue());
}
Expand Down Expand Up @@ -785,8 +723,8 @@ RequirementMatch swift::matchWitness(TypeChecker &tc,
};

// Match a type in the requirement to a type in the witness.
auto matchTypes = [&](Type reqType,
Type witnessType) -> Optional<RequirementMatch> {
auto matchTypes = [&](Type reqType, Type witnessType)
-> Optional<RequirementMatch> {
cs->addConstraint(ConstraintKind::Equal, reqType, witnessType, locator);
// FIXME: Check whether this has already failed.
return None;
Expand Down
45 changes: 45 additions & 0 deletions stdlib/public/core/ImplicitlyUnwrappedOptional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,48 @@ public enum ImplicitlyUnwrappedOptional<Wrapped> : ExpressibleByNilLiteral {
self = .none
}
}

#if _runtime(_ObjC)
extension ImplicitlyUnwrappedOptional : _ObjectiveCBridgeable {
@_inlineable // FIXME(sil-serialize-all)
public func _bridgeToObjectiveC() -> AnyObject {
switch self {
case .none:
_preconditionFailure("Attempt to bridge an implicitly unwrapped optional containing nil")

case .some(let x):
return Swift._bridgeAnythingToObjectiveC(x)
}
}

@_inlineable // FIXME(sil-serialize-all)
public static func _forceBridgeFromObjectiveC(
_ x: AnyObject,
result: inout ImplicitlyUnwrappedOptional<Wrapped>?
) {
result = Swift._forceBridgeFromObjectiveC(x, Wrapped.self)
}

@_inlineable // FIXME(sil-serialize-all)
public static func _conditionallyBridgeFromObjectiveC(
_ x: AnyObject,
result: inout ImplicitlyUnwrappedOptional<Wrapped>?
) -> Bool {
let bridged: Wrapped? =
Swift._conditionallyBridgeFromObjectiveC(x, Wrapped.self)
if let value = bridged {
result = value
}

return false
}

@_inlineable // FIXME(sil-serialize-all)
public static func _unconditionallyBridgeFromObjectiveC(_ source: AnyObject?)
-> Wrapped! {
var result: ImplicitlyUnwrappedOptional<Wrapped>?
_forceBridgeFromObjectiveC(source!, result: &result)
return result!
}
}
#endif
36 changes: 36 additions & 0 deletions test/Interpreter/dynamic_cast_optionals_to_nsobject.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: %target-run-simple-swift | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: objc_interop

import Foundation

// rdar://problem/36477954
func AnyToNSObject(_ a: Any) {
if a is NSObject {
// ok
} else {
fatalError("argument is not bridgable to NSObject")
}
}

let opt: String? = "hello"
AnyToNSObject(opt as Any)

let doubleOpt: String?? = "hello"
AnyToNSObject(doubleOpt as Any)

let iuo: String! = "goodbye"
AnyToNSObject(iuo as Any)

let doubleIUO: String!! = "goodbye"
AnyToNSObject(doubleIUO as Any)

// rdar://problem/36559165
let dict = NSMutableDictionary()
let kSomeKey: String! = "kSomeKey"
dict.setValue(true as Any, forKey: kSomeKey)
// CHECK: value: 1
print("value:", dict[kSomeKey] ?? "nil")

// CHECK: ok
print("ok")