@@ -179,9 +179,15 @@ StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
179
179
180
180
// / Returns the symbolic value that represents the "has_value" property of the
181
181
// / optional value `OptionalVal`. Returns null if `OptionalVal` is null.
182
- BoolValue *getHasValue (Value *OptionalVal) {
183
- if (OptionalVal) {
184
- return cast<BoolValue>(OptionalVal->getProperty (" has_value" ));
182
+ BoolValue *getHasValue (Environment &Env, Value *OptionalVal) {
183
+ if (OptionalVal != nullptr ) {
184
+ auto *HasValueVal =
185
+ cast_or_null<BoolValue>(OptionalVal->getProperty (" has_value" ));
186
+ if (HasValueVal == nullptr ) {
187
+ HasValueVal = &Env.makeAtomicBoolValue ();
188
+ OptionalVal->setProperty (" has_value" , *HasValueVal);
189
+ }
190
+ return HasValueVal;
185
191
}
186
192
return nullptr ;
187
193
}
@@ -218,6 +224,50 @@ int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
218
224
.getDesugaredType (ASTCtx));
219
225
}
220
226
227
+ // / Tries to initialize the `optional`'s value (that is, contents), and return
228
+ // / its location. Returns nullptr if the value can't be represented.
229
+ StorageLocation *maybeInitializeOptionalValueMember (QualType Q,
230
+ Value &OptionalVal,
231
+ Environment &Env) {
232
+ // The "value" property represents a synthetic field. As such, it needs
233
+ // `StorageLocation`, like normal fields (and other variables). So, we model
234
+ // it with a `ReferenceValue`, since that includes a storage location. Once
235
+ // the property is set, it will be shared by all environments that access the
236
+ // `Value` representing the optional (here, `OptionalVal`).
237
+ if (auto *ValueProp = OptionalVal.getProperty (" value" )) {
238
+ auto *ValueRef = clang::cast<ReferenceValue>(ValueProp);
239
+ auto &ValueLoc = ValueRef->getPointeeLoc ();
240
+ if (Env.getValue (ValueLoc) == nullptr ) {
241
+ // The property was previously set, but the value has been lost. This can
242
+ // happen, for example, because of an environment merge (where the two
243
+ // environments mapped the property to different values, which resulted in
244
+ // them both being discarded), or when two blocks in the CFG, with neither
245
+ // a dominator of the other, visit the same optional value, or even when a
246
+ // block is revisited during testing to collect per-statement state.
247
+ // FIXME: This situation means that the optional contents are not shared
248
+ // between branches and the like. Practically, this lack of sharing
249
+ // reduces the precision of the model when the contents are relevant to
250
+ // the check, like another optional or a boolean that influences control
251
+ // flow.
252
+ auto *ValueVal = Env.createValue (ValueLoc.getType ());
253
+ if (ValueVal == nullptr )
254
+ return nullptr ;
255
+ Env.setValue (ValueLoc, *ValueVal);
256
+ }
257
+ return &ValueLoc;
258
+ }
259
+
260
+ auto Ty = stripReference (Q);
261
+ auto *ValueVal = Env.createValue (Ty);
262
+ if (ValueVal == nullptr )
263
+ return nullptr ;
264
+ auto &ValueLoc = Env.createStorageLocation (Ty);
265
+ Env.setValue (ValueLoc, *ValueVal);
266
+ auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc);
267
+ OptionalVal.setProperty (" value" , Env.takeOwnership (std::move (ValueRef)));
268
+ return &ValueLoc;
269
+ }
270
+
221
271
void initializeOptionalReference (const Expr *OptionalExpr,
222
272
const MatchFinder::MatchResult &,
223
273
LatticeTransferState &State) {
@@ -233,11 +283,16 @@ void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
233
283
LatticeTransferState &State) {
234
284
if (auto *OptionalVal =
235
285
State.Env .getValue (*ObjectExpr, SkipPast::ReferenceThenPointer)) {
236
- auto *HasValueVal = getHasValue (OptionalVal);
237
- assert (HasValueVal != nullptr );
238
-
239
- if (State.Env .flowConditionImplies (*HasValueVal))
240
- return ;
286
+ if (State.Env .getStorageLocation (*UnwrapExpr, SkipPast::None) == nullptr )
287
+ if (auto *Loc = maybeInitializeOptionalValueMember (
288
+ UnwrapExpr->getType (), *OptionalVal, State.Env ))
289
+ State.Env .setStorageLocation (*UnwrapExpr, *Loc);
290
+
291
+ auto *Prop = OptionalVal->getProperty (" has_value" );
292
+ if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
293
+ if (State.Env .flowConditionImplies (*HasValueVal))
294
+ return ;
295
+ }
241
296
}
242
297
243
298
// Record that this unwrap is *not* provably safe.
@@ -258,12 +313,9 @@ void transferMakeOptionalCall(const CallExpr *E,
258
313
void transferOptionalHasValueCall (const CXXMemberCallExpr *CallExpr,
259
314
const MatchFinder::MatchResult &,
260
315
LatticeTransferState &State) {
261
- if (auto *OptionalVal = cast_or_null<StructValue>(
262
- State.Env .getValue (*CallExpr->getImplicitObjectArgument (),
263
- SkipPast::ReferenceThenPointer))) {
264
- auto *HasValueVal = getHasValue (OptionalVal);
265
- assert (HasValueVal != nullptr );
266
-
316
+ if (auto *HasValueVal = getHasValue (
317
+ State.Env , State.Env .getValue (*CallExpr->getImplicitObjectArgument (),
318
+ SkipPast::ReferenceThenPointer))) {
267
319
auto &CallExprLoc = State.Env .createStorageLocation (*CallExpr);
268
320
State.Env .setValue (CallExprLoc, *HasValueVal);
269
321
State.Env .setStorageLocation (*CallExpr, CallExprLoc);
@@ -284,12 +336,11 @@ void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
284
336
Result.Nodes .getNodeAs <clang::CXXMemberCallExpr>(ValueOrCallID)
285
337
->getImplicitObjectArgument ();
286
338
287
- auto *OptionalVal = cast_or_null<StructValue>(
288
- Env.getValue (*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
289
- if (OptionalVal == nullptr )
339
+ auto *HasValueVal = getHasValue (
340
+ State.Env ,
341
+ State.Env .getValue (*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
342
+ if (HasValueVal == nullptr )
290
343
return ;
291
- auto *HasValueVal = getHasValue (OptionalVal);
292
- assert (HasValueVal != nullptr );
293
344
294
345
auto *ExprValue = cast_or_null<BoolValue>(
295
346
State.Env .getValue (*ValueOrPredExpr, SkipPast::None));
@@ -376,8 +427,9 @@ getValueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
376
427
377
428
// This is a constructor/assignment call for `optional<T>` with argument of
378
429
// type `optional<U>` such that `T` is constructible from `U`.
379
- if (BoolValue *Val = getHasValue (State.Env .getValue (E, SkipPast::Reference)))
380
- return *Val;
430
+ if (auto *HasValueVal =
431
+ getHasValue (State.Env , State.Env .getValue (E, SkipPast::Reference)))
432
+ return *HasValueVal;
381
433
return State.Env .makeAtomicBoolValue ();
382
434
}
383
435
0 commit comments