mirror of
https://github.com/apple/swift.git
synced 2026-02-27 18:26:24 +01:00
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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user