Skip to content

Initial implementation of SE-0054 "Abolish IUO Type" #2322

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 8 commits into from
May 3, 2016
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
4 changes: 4 additions & 0 deletions benchmark/utils/ObjectiveCTests/ObjectiveCTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface BridgeTester : NSObject {
NSString *myString;
NSArray<NSString *> *myArrayOfStrings;
Expand All @@ -24,3 +26,5 @@
- (NSArray<NSString *> *)testToArrayOfStrings;

@end

NS_ASSUME_NONNULL_END
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1461,6 +1461,10 @@ NOTE(override_unnecessary_IUO_use_strict,none,
NOTE(override_unnecessary_IUO_silence,none,
"add parentheses to silence this warning", ())

ERROR(iuo_in_illegal_position,none,
"implicitly unwrapped optionals are only allowed at top level and as "
"function results", ())

ERROR(override_mutable_covariant_property,none,
"cannot override mutable property %0 of type %1 with covariant type %2",
(Identifier, Type, Type))
Expand Down
3 changes: 0 additions & 3 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,6 @@ namespace swift {
/// Enable the Swift 3 migration via Fix-Its.
bool Swift3Migration = false;

/// Allow IUO types to be inferred for otherwise untyped variables.
bool InferIUOs = true;

/// Sets the target we are building for and updates platform conditions
/// to match.
///
Expand Down
4 changes: 0 additions & 4 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,6 @@ def swift3_migration :
Flag<["-"], "swift3-migration">,
HelpText<"Enable Fix-It based migration aids for Swift 3">;

def disable_infer_iuos :
Flag<["-"], "disable-infer-iuos">,
HelpText<"Disable inferring IUO type for otherwise unconstrained variables">;

def warn_omit_needless_words :
Flag<["-"], "Womit-needless-words">,
HelpText<"Warn about needless words in names">;
Expand Down
9 changes: 6 additions & 3 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1325,13 +1325,15 @@ namespace {
auto storedUnderlyingType = Impl.importType(
Decl->getUnderlyingType(), ImportTypeKind::Value,
isInSystemModule(DC),
Decl->getUnderlyingType()->isBlockPointerType());
Decl->getUnderlyingType()->isBlockPointerType(),
OTK_Optional);

// Find a bridged type, which may be different
auto computedPropertyUnderlyingType = Impl.importType(
Decl->getUnderlyingType(), ImportTypeKind::Property,
isInSystemModule(DC),
Decl->getUnderlyingType()->isBlockPointerType());
Decl->getUnderlyingType()->isBlockPointerType(),
OTK_Optional);

if (storedUnderlyingType.getCanonicalTypeOrNull() ==
computedPropertyUnderlyingType.getCanonicalTypeOrNull()) {
Expand Down Expand Up @@ -1365,7 +1367,8 @@ namespace {
SwiftType = Impl.importType(ClangType,
ImportTypeKind::Typedef,
isInSystemModule(DC),
ClangType->isBlockPointerType());
ClangType->isBlockPointerType(),
OTK_Optional);
}

if (!SwiftType)
Expand Down
9 changes: 6 additions & 3 deletions lib/ClangImporter/ImportType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,8 @@ namespace {
auto resultTy = Impl.importType(type->getReturnType(),
ImportTypeKind::Result,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes);
CanFullyBridgeTypes,
OTK_Optional);
if (!resultTy)
return Type();

Expand All @@ -466,7 +467,8 @@ namespace {
param != paramEnd; ++param) {
auto swiftParamTy = Impl.importType(*param, ImportTypeKind::Parameter,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes);
CanFullyBridgeTypes,
OTK_Optional);
if (!swiftParamTy)
return Type();

Expand All @@ -489,7 +491,8 @@ namespace {
auto resultTy = Impl.importType(type->getReturnType(),
ImportTypeKind::Result,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes);
CanFullyBridgeTypes,
OTK_Optional);
if (!resultTy)
return Type();

Expand Down
3 changes: 0 additions & 3 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -760,9 +760,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.WarnOmitNeedlessWords = Args.hasArg(OPT_warn_omit_needless_words);
Opts.StripNSPrefix |= Args.hasArg(OPT_enable_strip_ns_prefix);
Opts.InferImportAsMember |= Args.hasArg(OPT_enable_infer_import_as_member);
if (Args.hasArg(OPT_disable_infer_iuos)) {
Opts.InferIUOs = false;
}

Opts.EnableThrowWithoutTry |= Args.hasArg(OPT_enable_throw_without_try);

Expand Down
19 changes: 8 additions & 11 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -875,18 +875,15 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
}
}

// Don't deduce IUO types.
Type alternateType;
ASTContext &ctx = cs.getTypeChecker().Context;
if (!ctx.LangOpts.InferIUOs) {
// Don't deduce IUO types.
if (kind == AllowedBindingKind::Supertypes &&
constraint->getKind() >= ConstraintKind::Conversion &&
constraint->getKind() <= ConstraintKind::OperatorArgumentConversion) {
if (auto objectType =
cs.lookThroughImplicitlyUnwrappedOptionalType(type)) {
type = OptionalType::get(objectType);
alternateType = objectType;
}
if (kind == AllowedBindingKind::Supertypes &&
constraint->getKind() >= ConstraintKind::Conversion &&
constraint->getKind() <= ConstraintKind::OperatorArgumentConversion) {
if (auto objectType =
cs.lookThroughImplicitlyUnwrappedOptionalType(type)) {
type = OptionalType::get(objectType);
alternateType = objectType;
}
}

Expand Down
61 changes: 61 additions & 0 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,64 @@ Type TypeChecker::resolveIdentifierType(
return result;
}

// Returns true if any illegal IUOs were found. If inference of IUO type is disabled, IUOs may only be specified in the following positions:
// * outermost type
// * function param
// * function return type
static bool checkForIllegalIUOs(TypeChecker &TC, TypeRepr *Repr,
TypeResolutionOptions Options) {
class IllegalIUOWalker : public ASTWalker {
TypeChecker &TC;
SmallVector<bool, 4> IUOsAllowed;
bool FoundIllegalIUO = false;

public:
IllegalIUOWalker(TypeChecker &TC, bool IsGenericParameter)
: TC(TC)
, IUOsAllowed{!IsGenericParameter} {}

bool walkToTypeReprPre(TypeRepr *T) {
bool iuoAllowedHere = IUOsAllowed.back();

// Raise a diagnostic if we run into a prohibited IUO.
if (!iuoAllowedHere) {
if (auto *iuoTypeRepr =
dyn_cast<ImplicitlyUnwrappedOptionalTypeRepr>(T)) {
TC.diagnose(iuoTypeRepr->getStartLoc(), diag::iuo_in_illegal_position)
.fixItReplace(iuoTypeRepr->getExclamationLoc(), "?");
FoundIllegalIUO = true;
}
}

bool childIUOsAllowed = false;
if (iuoAllowedHere) {
if (auto *tupleTypeRepr = dyn_cast<TupleTypeRepr>(T)) {
if (tupleTypeRepr->isParenType()) {
childIUOsAllowed = true;
}
} else if (isa<FunctionTypeRepr>(T)) {
childIUOsAllowed = true;
} else if (isa<AttributedTypeRepr>(T) || isa<InOutTypeRepr>(T)) {
childIUOsAllowed = true;
}
}
IUOsAllowed.push_back(childIUOsAllowed);
return true;
}

bool walkToTypeReprPost(TypeRepr *T) {
IUOsAllowed.pop_back();
return true;
}

bool getFoundIllegalIUO() const { return FoundIllegalIUO; }
};

IllegalIUOWalker Walker(TC, Options.contains(TR_GenericSignature));
Repr->walk(Walker);
return Walker.getFoundIllegalIUO();
}

bool TypeChecker::validateType(TypeLoc &Loc, DeclContext *DC,
TypeResolutionOptions options,
GenericTypeResolver *resolver,
Expand All @@ -1351,6 +1409,9 @@ bool TypeChecker::validateType(TypeLoc &Loc, DeclContext *DC,
return Loc.isError();

if (Loc.getType().isNull()) {
// Raise error if we parse an IUO type in an illegal position.
checkForIllegalIUOs(*this, Loc.getTypeRepr(), options);

auto type = resolveType(Loc.getTypeRepr(), DC, options, resolver,
unsatisfiedDependency);
if (!type) {
Expand Down
2 changes: 1 addition & 1 deletion stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ func _printDebuggingAdvice(_ fullTestName: String) {
var invocation = [Process.arguments[0]]
let interpreter = getenv("SWIFT_INTERPRETER")
if interpreter != nil {
if let interpreterCmd = String(validatingUTF8: interpreter) {
if let interpreterCmd = String(validatingUTF8: interpreter!) {
invocation.insert(interpreterCmd, at: 0)
}
}
Expand Down
2 changes: 1 addition & 1 deletion stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public func spawnChild(_ args: [String])
childArgs.insert(Process.arguments[0], at: 0)
let interpreter = getenv("SWIFT_INTERPRETER")
if interpreter != nil {
if let invocation = String(validatingUTF8: interpreter) {
if let invocation = String(validatingUTF8: interpreter!) {
childArgs.insert(invocation, at: 0)
}
}
Expand Down
4 changes: 2 additions & 2 deletions stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ internal func getReflectionInfoForImage(atIndex i: UInt32) -> ReflectionInfo? {
let header = unsafeBitCast(_dyld_get_image_header(i),
to: UnsafePointer<MachHeader>.self)

let imageName = _dyld_get_image_name(i)
let imageName = _dyld_get_image_name(i)!
if let fieldmd = getSectionInfo("__swift3_fieldmd", header) {
let assocty = getSectionInfo("__swift3_assocty", header)
let builtin = getSectionInfo("__swift3_builtin", header)
Expand Down Expand Up @@ -189,7 +189,7 @@ internal func sendReflectionInfos() {

internal func printErrnoAndExit() {
debugLog("BEGIN \(#function)"); defer { debugLog("END \(#function)") }
let errorCString = strerror(errno)
let errorCString = strerror(errno)!
let message = String(validatingUTF8: errorCString)! + "\n"
let bytes = Array(message.utf8)
fwrite(bytes, 1, bytes.count, stderr)
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/SDK/ObjectiveC/ObjectiveC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ extension Selector : CustomStringConvertible {
if name == nil {
return "<NULL>"
}
return String(cString: name)
return String(cString: name!)
}
}

Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/FloatingPointParsing.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ extension ${Self} {
_swift_stdlib_strto${cFuncSuffix2[bits]}_clocale(
chars, UnsafeMutablePointer($0))
}
return (result, endPtr == nil ? 0 : UnsafePointer(endPtr) - chars)
return (result, endPtr == nil ? 0 : UnsafePointer(endPtr!) - chars)
}

let (result, n) = text.withCString(parseNTBS)
Expand Down
6 changes: 3 additions & 3 deletions stdlib/public/core/ImplicitlyUnwrappedOptional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ extension ImplicitlyUnwrappedOptional : _ObjectiveCBridgeable {

public static func _forceBridgeFromObjectiveC(
_ x: AnyObject,
result: inout Wrapped!?
result: inout ImplicitlyUnwrappedOptional<Wrapped>?
) {
result = Swift._forceBridgeFromObjectiveC(x, Wrapped.self)
}

public static func _conditionallyBridgeFromObjectiveC(
_ x: AnyObject,
result: inout Wrapped!?
result: inout ImplicitlyUnwrappedOptional<Wrapped>?
) -> Bool {
let bridged: Wrapped? =
Swift._conditionallyBridgeFromObjectiveC(x, Wrapped.self)
Expand All @@ -115,7 +115,7 @@ extension ImplicitlyUnwrappedOptional : _ObjectiveCBridgeable {

public static func _unconditionallyBridgeFromObjectiveC(_ source: AnyObject?)
-> Wrapped! {
var result: Wrapped!?
var result: ImplicitlyUnwrappedOptional<Wrapped>?
_forceBridgeFromObjectiveC(source!, result: &result)
return result!
}
Expand Down
26 changes: 0 additions & 26 deletions test/1_stdlib/Optional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -379,20 +379,6 @@ OptionalTests.test("Optional OutputStream") {
expectEqual(String(optNoString), "Optional(main.TestNoString)")
expectEqual(debugPrintStr(optNoString), "Optional(main.TestNoString)")

let iouNoString: TestNoString! = TestNoString()
// IUO directly conforms to CustomStringConvertible.
// Disabled pending SR-164
// expectTrue(iouNoString is CustomStringConvertible)
expectTrue(canGenericCast(iouNoString, CustomStringConvertible.self))
expectFalse(iouNoString is Streamable)
expectFalse(canGenericCast(iouNoString, Streamable.self))
// CustomDebugStringConvertible conformance is a temporary hack.
// Disabled pending SR-164
// expectTrue(iouNoString is CustomDebugStringConvertible)
expectTrue(canGenericCast(iouNoString, CustomDebugStringConvertible.self))
expectEqual(String(iouNoString), "main.TestNoString")
expectEqual(debugPrintStr(iouNoString), "main.TestNoString")

let optString: TestString? = TestString()
expectTrue(optString is CustomStringConvertible)
expectTrue(canGenericCast(optString, CustomStringConvertible.self))
Expand All @@ -402,18 +388,6 @@ OptionalTests.test("Optional OutputStream") {
expectEqual(String(optString), "Optional(XString)")
expectEqual(debugPrintStr(optString), "Optional(XString)")

let iouString: TestString! = TestString()
expectTrue(iouString is CustomStringConvertible)
expectTrue(canGenericCast(iouString, CustomStringConvertible.self))
// CustomDebugStringConvertible conformance is a temporary hack.
expectTrue(iouString is CustomDebugStringConvertible)
expectTrue(canGenericCast(iouString, CustomDebugStringConvertible.self))
expectEqual(String(iouString), "AString")
// FIXME: Ideally the debug output would be "XString", but a reasonable
// implementation of that behavior requires conditional conformance.
// (directly invoking debugPrint(Any) already works correctly).
expectEqual(debugPrintStr(iouString), "AString")

let optStream: TestStream? = TestStream()
expectTrue(optStream is Streamable)
expectTrue(canGenericCast(optStream, Streamable.self))
Expand Down
1 change: 0 additions & 1 deletion test/1_stdlib/PrintString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ PrintTests.test("Printable") {
}

PrintTests.test("Printable") {
expectPrinted("meow", String!("meow"))
expectPrinted("Optional(\"meow\")", String?("meow"))
}

Expand Down
10 changes: 5 additions & 5 deletions test/ClangModules/Inputs/sdk-bridging-header.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
@class NSArray;

@interface Predicate : NSObject
+ (Predicate *)truePredicate;
+ (Predicate *)not;
+ (Predicate *)and:(NSArray *)subpredicates;
+ (Predicate *)or:(NSArray *)subpredicates;
@end
+ (nonnull Predicate *)truePredicate;
+ (nonnull Predicate *)not;
+ (nonnull Predicate *)and:(nonnull NSArray *)subpredicates;
+ (nonnull Predicate *)or:(nonnull NSArray *)subpredicates;
@end
12 changes: 7 additions & 5 deletions test/ClangModules/blocks_parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,23 @@ func testNoEscape(f: @noescape @convention(block) () -> Void, nsStr: NSString,
// rdar://problem/19818617
nsStr.enumerateLines(fStr) // okay due to @noescape

_ = nsStr.enumerateLines as Int // expected-error{{cannot convert value of type '(@noescape (String!) -> Void) -> Void' to type 'Int' in coercion}}
_ = nsStr.enumerateLines as Int // expected-error{{cannot convert value of type '(@noescape (String) -> Void) -> Void' to type 'Int' in coercion}}
}

func checkTypeImpl<T>(_ a: inout T, _: T.Type) {}
do {
var block = blockWithoutNullability()
checkTypeImpl(&block, ImplicitlyUnwrappedOptional<dispatch_block_t>.self)
var blockOpt = blockWithoutNullability()
checkTypeImpl(&blockOpt, Optional<dispatch_block_t>.self)
var block: dispatch_block_t = blockWithoutNullability()
}
do {
var block = blockWithNonnull()
checkTypeImpl(&block, dispatch_block_t.self)
}
do {
var block = blockWithNullUnspecified()
checkTypeImpl(&block, ImplicitlyUnwrappedOptional<dispatch_block_t>.self)
var blockOpt = blockWithNullUnspecified()
checkTypeImpl(&blockOpt, Optional<dispatch_block_t>.self)
var block: dispatch_block_t = blockWithNullUnspecified()
}
do {
var block = blockWithNullable()
Expand Down
Loading