diff --git a/clang/lib/Sema/SemaBounds.cpp b/clang/lib/Sema/SemaBounds.cpp index 8f69236ea063..f2fc9ce28213 100644 --- a/clang/lib/Sema/SemaBounds.cpp +++ b/clang/lib/Sema/SemaBounds.cpp @@ -4890,7 +4890,8 @@ namespace { // to LValue means that a member expression is being used to write to // memory), get the set of AbstractSets whose target bounds depend on // LValue. The observed bounds of each of these AbstractSets are recorded - // in ObservedBounds. + // in ObservedBounds. If they are not already present in ObservedBounds, + // their observed bounds are initialized to their target bounds. MemberExpr *M = GetAssignmentTargetMemberExpr(LValue); if (M) { AbstractSetSetTy AbstractSets; @@ -4900,9 +4901,18 @@ namespace { DumpSynthesizedMemberAbstractSets(llvm::outs(), AbstractSets); for (const AbstractSet *A : AbstractSets) { - BoundsExpr *TargetBounds = - S.GetLValueDeclaredBounds(A->GetRepresentative(), CSS); - State.ObservedBounds[A] = TargetBounds; + // We only set the observed bounds of A to the target bounds of A + // if A is not already present in ObservedBounds. If A is already + // present in ObservedBounds, then there was an assignment in the + // current top-level statement (or bundled block) that updated the + // observed bounds of A. These observed bounds may or may not depend + // on LValue. If A currently has observed bounds that do not use the + // value of LValue, then the current assignment to LValue should + // have no effect on the observed bounds of A. + if (!State.ObservedBounds.count(A)) { + State.ObservedBounds[A] = + S.GetLValueDeclaredBounds(A->GetRepresentative(), CSS); + } } } diff --git a/clang/test/CheckedC/inferred-bounds/bounds-context-members.c b/clang/test/CheckedC/inferred-bounds/bounds-context-members.c index d1abd17cbb38..78e044ef0f00 100644 --- a/clang/test/CheckedC/inferred-bounds/bounds-context-members.c +++ b/clang/test/CheckedC/inferred-bounds/bounds-context-members.c @@ -8,14 +8,14 @@ struct S { int len; - array_ptr p : count(len); // expected-note 4 {{(expanded) declared bounds are 'bounds(s->p, s->p + s->len)'}} + array_ptr p : count(len); // expected-note 5 {{(expanded) declared bounds are 'bounds(s->p, s->p + s->len)'}} int i; array_ptr q : count(i); // expected-note 2 {{(expanded) declared bounds are 'bounds(s[3].q, s[3].q + s[3].i)'}} \ // expected-note {{(expanded) declared bounds are 'bounds(s[4].q, s[4].q + s[4].i)'}} array_ptr r : count(i); // expected-note 2 {{(expanded) declared bounds are 'bounds(s[3].r, s[3].r + s[3].i)'}} array_ptr f : count(3); // expected-note 2 {{(expanded) declared bounds are 'bounds(s->f, s->f + 3)'}} array_ptr g : bounds(f, f + 3); // expected-note 2 {{(expanded) declared bounds are 'bounds(s->f, s->f + 3)'}} - array_ptr a : count(2); + array_ptr a : count(2); // expected-note 2 {{(expanded) declared bounds are 'bounds(s->a, s->a + 2)'}} array_ptr b : count(2); }; @@ -360,6 +360,115 @@ void updated_source_bounds3(struct S *s) { // CHECK-NEXT: } } +void multiple_assignments1(struct S *s, _Array_ptr arr : count(3)) { + // Observed bounds context after statement: { s->p => bounds(arr, arr + 3), arr => bounds(arr, arr + 3) } + s->p = arr, s->len = 3; + // CHECK: Statement S: + // CHECK-NEXT: BinaryOperator {{.*}} ',' + // CHECK: Observed bounds context after checking S: + // CHECK-NEXT: { + // CHECK-NEXT: LValue Expression: + // CHECK-NEXT: MemberExpr {{.*}} ->p + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 's' + // CHECK-NEXT: Bounds: + // CHECK-NEXT: RangeBoundsExpr + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'arr' + // CHECK-NEXT: BinaryOperator {{.*}} '+' + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'arr' + // CHECK-NEXT: IntegerLiteral {{.*}} 3 + // CHECK-NEXT: LValue Expression: + // CHECK-NEXT: DeclRefExpr {{.*}} 'arr' + // CHECK-NEXT: Bounds: + // CHECK-NEXT: RangeBoundsExpr + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'arr' + // CHECK-NEXT: BinaryOperator {{.*}} '+' + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'arr' + // CHECK-NEXT: IntegerLiteral {{.*}} 3 + // CHECK-NEXT: } + + // Observed bounds context after assignment: { s->p => bounds(unknown), arr => bounds(arr, arr + 3) } + s[0].len = 0; // expected-error {{inferred bounds for 's->p' are unknown after assignment}} \ + // expected-note {{lost the value of the expression 's[0].len' which is used in the (expanded) inferred bounds 'bounds(s->p, s->p + s->len)' of 's->p'}} + // CHECK: Statement S: + // CHECK-NEXT: BinaryOperator {{.*}} '=' + // CHECK: Observed bounds context after checking S: + // CHECK-NEXT: { + // CHECK-NEXT: LValue Expression: + // CHECK-NEXT: MemberExpr {{.*}} ->p + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 's' + // CHECK-NEXT: Bounds: + // CHECK-NEXT: NullaryBoundsExpr {{.*}} Unknown + // CHECK-NEXT: LValue Expression: + // CHECK-NEXT: DeclRefExpr {{.*}} 'arr' + // CHECK-NEXT: Bounds: + // CHECK-NEXT: RangeBoundsExpr + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'arr' + // CHECK-NEXT: BinaryOperator {{.*}} '+' + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'arr' + // CHECK-NEXT: IntegerLiteral {{.*}} 3 + // CHECK-NEXT: } +} + +void multiple_assignments2(struct S *s) { + // Observed bounds context after statement: { s->a => bounds(s->a - 1, (s->a - 1) + 2) } + s->a = s->b, s->a++; // expected-warning {{cannot prove declared bounds for 's->a' are valid after increment}} \ + // expected-note {{(expanded) inferred bounds are 'bounds(s->a - 1, s->a - 1 + 2)'}} + // CHECK: Statement S: + // CHECK-NEXT: BinaryOperator {{.*}} ',' + // CHECK: Observed bounds context after checking S: + // CHECK-NEXT: { + // CHECK-NEXT: LValue Expression: + // CHECK-NEXT: MemberExpr {{.*}} ->a + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 's' + // CHECK-NEXT: Bounds: + // CHECK-NEXT: RangeBoundsExpr + // CHECK-NEXT: BinaryOperator {{.*}} '-' + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: MemberExpr {{.*}} ->a + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 's' + // CHECK-NEXT: IntegerLiteral {{.*}} 1 + // CHECK-NEXT: BinaryOperator {{.*}} '+' + // CHECK-NEXT: BinaryOperator {{.*}} '-' + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: MemberExpr {{.*}} ->a + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 's' + // CHECK-NEXT: IntegerLiteral {{.*}} 1 + // CHECK-NEXT: IntegerLiteral {{.*}} 2 + // CHECK-NEXT: } + + // Observed bounds context after statement: { s->a => bounds(unknown), s->b = bounds(any) } + s->a = s->b, s->b = 0; // expected-error {{inferred bounds for 's->a' are unknown after assignment}} \ + // expected-note {{lost the value of the expression 's->b' which is used in the (expanded) inferred bounds 'bounds(s->b, s->b + 2)' of 's->a'}} + // CHECK: Statement S: + // CHECK-NEXT: BinaryOperator {{.*}} ',' + // CHECK: Observed bounds context after checking S: + // CHECK-NEXT: { + // CHECK-NEXT: LValue Expression: + // CHECK-NEXT: MemberExpr {{.*}} ->a + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 's' + // CHECK-NEXT: Bounds: + // CHECK-NEXT: NullaryBoundsExpr {{.*}} Unknown + // CHECK-NEXT: LValue Expression: + // CHECK-NEXT: MemberExpr {{.*}} ->b + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 's' + // CHECK-NEXT: Bounds: + // CHECK-NEXT: NullaryBoundsExpr {{.*}} Any + // CHECK-NEXT: } +} + struct C { int len; array_ptr r : count(len); // expected-note {{(expanded) declared bounds are 'bounds(a.b.c.r, a.b.c.r + a.b.c.len)'}}