fix <rdar://problem/19782264> Immutable, optional class members can't have their subproperties read from during init()

The problem here was that the _preconditionImplicitlyUnwrappedOptionalHasValue
compiler intrinsic was taking the optional/IUO argument as inout as a performance
optimization, but DI would reject it (in narrow cases, in inits) because the inout
argument looks like a mutation.  

We could rework this to take it as an @in argument or something, but it is better
to just define this problem away: the precondition doesn't actually care about the
optional, it is just testing its presence, which SILGen does all the time.  Have
SILGen open code the switch_enum and just have the stdlib provide a simpler
_diagnoseUnexpectedNilOptional() to produce the error message.

This avoids the problem completely and produces slightly better -O0 codegen.



Swift SVN r25254
This commit is contained in:
Chris Lattner
2015-02-12 22:35:51 +00:00
parent 38a4f0c9fe
commit 9c7417edc2
8 changed files with 47 additions and 66 deletions

View File

@@ -395,11 +395,6 @@ public:
FuncDecl *get##Name(LazyResolver *resolver) const;
#include "swift/AST/KnownDecls.def"
/// Retrieve the declaration of
/// Swift._precondition{,ImplicitlyUnwrapped}OptionalHasValue.
FuncDecl *getPreconditionOptionalHasValueDecl(LazyResolver *resolver,
OptionalTypeKind kind) const;
/// Swift._does{,ImplicitlyUnwrapped}OptionalHaveValueAsBool.
FuncDecl *getDoesOptionalHaveValueAsBoolDecl(LazyResolver *resolver,
OptionalTypeKind kind) const;

View File

@@ -56,7 +56,7 @@ FUNC_DECL(ForceBridgeFromObjectiveC,
FUNC_DECL(ConditionallyBridgeFromObjectiveC,
"_conditionallyBridgeFromObjectiveC")
FUNC_DECL(DidEnterMain,
"_didEnterMain")
FUNC_DECL(DidEnterMain, "_didEnterMain")
FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional")
#undef FUNC_DECL

View File

@@ -870,34 +870,6 @@ static unsigned asIndex(OptionalTypeKind optionalKind) {
? (PREFIX "Optional" SUFFIX) \
: (PREFIX "ImplicitlyUnwrappedOptional" SUFFIX))
FuncDecl *ASTContext::getPreconditionOptionalHasValueDecl(
LazyResolver *resolver, OptionalTypeKind optionalKind) const {
auto &cache = Impl.PreconditionOptionalHasValueDecls[asIndex(optionalKind)];
if (cache) return cache;
auto name
= getOptionalIntrinsicName("_precondition", optionalKind, "HasValue");
// Look for a generic function.
CanType input, output, param;
auto decl = findLibraryIntrinsic(*this, name, resolver);
if (!decl || !isGenericIntrinsic(decl, input, output, param))
return nullptr;
// Input must be inout Optional<T>.
auto inputInOut = dyn_cast<InOutType>(input);
if (!inputInOut || !isOptionalType(*this, optionalKind,
inputInOut.getObjectType(), param))
return nullptr;
// Output must be ().
if (output != CanType(TheEmptyTupleType))
return nullptr;
cache = decl;
return decl;
}
FuncDecl *ASTContext::getDoesOptionalHaveValueAsBoolDecl(
LazyResolver *resolver, OptionalTypeKind optionalKind) const {
auto &cache = Impl.DoesOptionalHaveValueAsBoolDecls[asIndex(optionalKind)];
@@ -954,8 +926,7 @@ FuncDecl *ASTContext::getGetOptionalValueDecl(LazyResolver *resolver,
static bool hasOptionalIntrinsics(const ASTContext &ctx, LazyResolver *resolver,
OptionalTypeKind optionalKind) {
return ctx.getPreconditionOptionalHasValueDecl(resolver, optionalKind) &&
ctx.getGetOptionalValueDecl(resolver, optionalKind);
return ctx.getGetOptionalValueDecl(resolver, optionalKind);
}
bool ASTContext::hasOptionalIntrinsics(LazyResolver *resolver) const {

View File

@@ -1981,18 +1981,29 @@ void SILGenFunction::emitInjectOptionalNothingInto(SILLocation loc,
void SILGenFunction::emitPreconditionOptionalHasValue(SILLocation loc,
SILValue addr) {
SILType optType = addr.getType().getObjectType();
OptionalTypeKind optionalKind;
CanType valueType = getOptionalValueType(optType, optionalKind);
OptionalTypeKind OTK;
getOptionalValueType(addr.getType().getObjectType(), OTK);
FuncDecl *fn =
getASTContext().getPreconditionOptionalHasValueDecl(nullptr, optionalKind);
Substitution sub = getSimpleSubstitution(fn, valueType);
// Generate code to the optional is present, and if not abort with a message
// (provided by the stdlib).
SILBasicBlock *failBB = createBasicBlock(), *contBB = createBasicBlock();
// The argument to _preconditionOptionalHasValue is passed by reference.
emitApplyOfLibraryIntrinsic(loc, fn, sub,
ManagedValue::forUnmanaged(addr),
SGFContext());
auto NoneEnumElementDecl = getASTContext().getOptionalNoneDecl(OTK);
B.createSwitchEnumAddr(loc, addr, /*defaultDest*/contBB,
{ { NoneEnumElementDecl, failBB }});
B.emitBlock(failBB);
// Call the standard library implementation of _diagnoseUnexpectedNilOptional.
if (auto diagnoseFailure =
getASTContext().getDiagnoseUnexpectedNilOptional(nullptr)) {
emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, {}, {},
SGFContext());
}
B.createUnreachable(loc);
B.clearInsertionPoint();
B.emitBlock(contBB);
}
SILValue SILGenFunction::emitDoesOptionalHaveValue(SILLocation loc,

View File

@@ -77,19 +77,6 @@ extension ImplicitlyUnwrappedOptional : Printable {
}
}
// Intrinsics for use by language features.
@transparent
public // COMPILER_INTRINSIC
func _preconditionImplicitlyUnwrappedOptionalHasValue<T>(inout v: T!) {
switch v {
case .Some:
break
case .None:
_preconditionFailure(
"unexpectedly found nil while unwrapping an Optional value")
}
}
@transparent
public // COMPILER_INTRINSIC
func _getImplicitlyUnwrappedOptionalValue<T>(v: T!) -> T {

View File

@@ -87,8 +87,8 @@ func _doesOptionalHaveValueAsBool<T>(v: T?) -> Bool {
@transparent
public // COMPILER_INTRINSIC
func _preconditionOptionalHasValue<T>(inout v: T?) {
_precondition(v != nil,
func _diagnoseUnexpectedNilOptional() {
_preconditionFailure(
"unexpectedly found nil while unwrapping an Optional value")
}

View File

@@ -2,8 +2,8 @@
// CHECK-LABEL: sil hidden @_TF15optional_lvalue22assign_optional_lvalueFTRGSqSi_Si_T_
// CHECK: [[SHADOW:%.*]] = alloc_box $Optional<Int>
// CHECK: [[PRECOND:%.*]] = function_ref @_TFSs29_preconditionOptionalHasValueU__FRGSqQ__T_
// CHECK: apply [transparent] [[PRECOND]]<Int>([[SHADOW]]#1)
// CHECK: [[PRECOND:%.*]] = function_ref @_TFSs30_diagnoseUnexpectedNilOptionalFT_T_
// CHECK: apply [transparent] [[PRECOND]]()
// CHECK: [[PAYLOAD:%.*]] = unchecked_take_enum_data_addr [[SHADOW]]#1 : $*Optional<Int>, #Optional.Some!enumelt.1
// CHECK: assign {{%.*}} to [[PAYLOAD]]
func assign_optional_lvalue(inout x: Int?, y: Int) {
@@ -12,8 +12,8 @@ func assign_optional_lvalue(inout x: Int?, y: Int) {
// CHECK-LABEL: sil hidden @_TF15optional_lvalue17assign_iuo_lvalueFTRGSQSi_Si_T_
// CHECK: [[SHADOW:%.*]] = alloc_box $ImplicitlyUnwrappedOptional<Int>
// CHECK: [[PRECOND:%.*]] = function_ref @_TFSs48_preconditionImplicitlyUnwrappedOptionalHasValueU__FRGSQQ__T_
// CHECK: apply [transparent] [[PRECOND]]<Int>([[SHADOW]]#1)
// CHECK: [[PRECOND:%.*]] = function_ref @_TFSs30_diagnoseUnexpectedNilOptionalFT_T_
// CHECK: apply [transparent] [[PRECOND]]()
// CHECK: [[PAYLOAD:%.*]] = unchecked_take_enum_data_addr [[SHADOW]]#1 : $*ImplicitlyUnwrappedOptional<Int>, #ImplicitlyUnwrappedOptional.Some!enumelt.1
// CHECK: assign {{%.*}} to [[PAYLOAD]]
func assign_iuo_lvalue(inout x: Int!, y: Int) {

View File

@@ -1003,3 +1003,20 @@ struct StructMutatingMethodTest {
@transparent
func myTransparentFunction(inout x : Int) {}
// <rdar://problem/19782264> Immutable, optional class members can't have their subproperties read from during init()
class MyClassWithAnInt {
let channelCount : Int = 42
}
class MyClassTestExample {
let clientFormat : MyClassWithAnInt!
init(){
clientFormat = MyClassWithAnInt()
let channels = clientFormat.channelCount
}
}