fix <rdar://problem/19259730> Using mutating methods in a struct initializer with a let property is rejected

while we're at it, improve the QoI for actually-invalid mutating method calls in struct initalizers.


Swift SVN r24030
This commit is contained in:
Chris Lattner
2014-12-19 06:52:37 +00:00
parent c11eb40008
commit 3a7d8df92b
5 changed files with 59 additions and 3 deletions

View File

@@ -92,6 +92,10 @@ ERROR(variable_escape_before_initialized,sil_analysis,none,
ERROR(self_use_before_init_in_delegatinginit,sil_analysis,none, ERROR(self_use_before_init_in_delegatinginit,sil_analysis,none,
"use of '%0' in delegating initializer before self.init is called", "use of '%0' in delegating initializer before self.init is called",
(StringRef)) (StringRef))
ERROR(self_use_before_init_in_structinit,sil_analysis,none,
"use of '%0' in delegating initializer before self.init is called",
(StringRef))
ERROR(variable_addrtaken_before_initialized,sil_analysis,none, ERROR(variable_addrtaken_before_initialized,sil_analysis,none,
"address of variable '%0' taken before it is initialized", "address of variable '%0' taken before it is initialized",
(StringRef)) (StringRef))

View File

@@ -662,10 +662,19 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) {
continue; continue;
// If this is an @inout parameter, it is like both a load and store. // If this is an @inout parameter, it is like both a load and store.
case ParameterConvention::Indirect_Inout: case ParameterConvention::Indirect_Inout: {
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::InOutUse); // If we're in the initializer for a struct, and this is a call to a
// mutating method, we model that as an escape of self. If an
// individual sub-member is passed as inout, then we model that as an
// inout use.
auto Kind = DIUseKind::InOutUse;
if (TheMemory.isStructInitSelf() && Pointer == TheMemory.getAddress())
Kind = DIUseKind::Escape;
addElementUses(BaseEltNo, PointeeType, User, Kind);
continue; continue;
} }
}
llvm_unreachable("bad parameter convention"); llvm_unreachable("bad parameter convention");
} }

View File

@@ -106,6 +106,14 @@ public:
return true; return true;
return false; return false;
} }
// True if the memory object is the 'self' argument of a struct initializer.
bool isStructInitSelf() const {
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
if (MUI->isRootSelf() && isa<StructDecl>(getType()->getAnyNominal()))
return true;
return false;
}
// True if the memory object is the 'self' argument of a class initializer. // True if the memory object is the 'self' argument of a class initializer.
bool isClassInitSelf() const { bool isClassInitSelf() const {

View File

@@ -673,7 +673,13 @@ void LifetimeChecker::doIt() {
diag::return_from_init_without_self_init); diag::return_from_init_without_self_init);
break; break;
} }
} else if (isa<ApplyInst>(Inst) && TheMemory.isStructInitSelf()) {
if (shouldEmitError(Inst)) {
diagnose(Module, Inst->getLoc(),
diag::use_of_self_before_fully_init);
noteUninitializedMembers(Use);
}
break;
} else if (isa<MarkFunctionEscapeInst>(Inst)) } else if (isa<MarkFunctionEscapeInst>(Inst))
DiagMessage = diag::global_variable_function_use_uninit; DiagMessage = diag::global_variable_function_use_uninit;
else if (isa<AddressToPointerInst>(Inst)) else if (isa<AddressToPointerInst>(Inst))

View File

@@ -957,3 +957,32 @@ class r19254812Derived: r19254812Base{
println(pi) // ok, no diagnostic expected. println(pi) // ok, no diagnostic expected.
} }
} }
// <rdar://problem/19259730> Using mutating methods in a struct initializer with a let property is rejected
struct StructMutatingMethodTest {
let a, b: String // expected-note 2 {{'self.b' not initialized}}
init(x: String, y: String) {
a = x
b = y
mutate() // ok
}
init(x: String) {
a = x
mutate() // expected-error {{'self' used before all stored properties are initialized}}
b = x
}
init() {
a = ""
nonmutate() // expected-error {{'self' used before all stored properties are initialized}}
b = ""
}
mutating func mutate() {}
func nonmutate() {}
}