Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -5733,12 +5733,6 @@ class Sema final {
// normalized bounds for D.
BoundsExpr *NormalizeBounds(const VarDecl *D);

// This is wrapper around CheckBoundsDeclaration::ExpandToRange. This
// provides an easy way to invoke this function from outside the class. Given
// a byte_count or count bounds expression for the VarDecl D, ExpandToRange
// will expand it to a range bounds expression.
BoundsExpr *ExpandBoundsToRange(const VarDecl *D, const BoundsExpr *B);

// Returns the declared bounds for the lvalue expression E. Assignments
// to E must satisfy these bounds. After checking a top-level statement,
// the inferred bounds of E must imply these declared bounds.
Expand Down
70 changes: 37 additions & 33 deletions clang/lib/Sema/SemaBounds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6998,48 +6998,52 @@ void Sema::WarnDynamicCheckAlwaysFails(const Expr *Condition) {
// range bounds are attached to the VarDecl D to avoid recomputing the
// normalized bounds for D.
BoundsExpr *Sema::NormalizeBounds(const VarDecl *D) {
// Do not attempt to normalize bounds for invalid declarations.
if (D->isInvalidDecl())
return nullptr;

// If D already has a normalized bounds expression, do not recompute it.
if (BoundsExpr *NormalizedBounds = D->getNormalizedBounds())
return NormalizedBounds;

// Normalize the bounds of D to a RangeBoundsExpr and attach the normalized
// bounds to D to avoid recomputing them.
BoundsExpr *Bounds = ExpandBoundsToRange(D, D->getBoundsExpr());
D->setNormalizedBounds(Bounds);
return Bounds;
}
// Expand the bounds expression of D to a RangeBoundsExpr if possible.
const BoundsExpr *B = D->getBoundsExpr();
BoundsExpr *ExpandedBounds = nullptr;

// This is wrapper around CheckBoundsDeclaration::ExpandToRange. This provides
// an easy way to invoke this function from outside the class. Given a
// byte_count or count bounds expression for the VarDecl D, ExpandToRange will
// expand it to a range bounds expression.
BoundsExpr *Sema::ExpandBoundsToRange(const VarDecl *D, const BoundsExpr *B) {
// If the bounds expr is already a RangeBoundsExpr, simply return it.
// If the bounds expr of D is already a RangeBoundsExpr, there is
// no need to expand it.
if (B && isa<RangeBoundsExpr>(B))
return const_cast<BoundsExpr *>(B);
ExpandedBounds = const_cast<BoundsExpr *>(B);
else {
PrepassInfo Info;
std::pair<ComparisonSet, ComparisonSet> EmptyFacts;
CheckBoundsDeclarations CBD = CheckBoundsDeclarations(*this, Info, EmptyFacts);

PrepassInfo Info;
std::pair<ComparisonSet, ComparisonSet> EmptyFacts;
CheckBoundsDeclarations CBD = CheckBoundsDeclarations(*this, Info, EmptyFacts);
if (D->getType()->isArrayType()) {
ExprResult ER = BuildDeclRefExpr(const_cast<VarDecl *>(D), D->getType(),
clang::ExprValueKind::VK_LValue,
SourceLocation());
if (ER.isInvalid())
return nullptr;
Expr *Base = ER.get();

if (D->getType()->isArrayType()) {
ExprResult ER = BuildDeclRefExpr(const_cast<VarDecl *>(D), D->getType(),
clang::ExprValueKind::VK_LValue,
SourceLocation());
if (ER.isInvalid())
return nullptr;
Expr *Base = ER.get();

// Declared bounds override the bounds based on the array type.
if (!B)
return CBD.ArrayExprBounds(Base);
Base = CBD.CreateImplicitCast(Context.getDecayedType(Base->getType()),
CastKind::CK_ArrayToPointerDecay,
Base);
return CBD.ExpandToRange(Base, const_cast<BoundsExpr *>(B));
// Declared bounds override the bounds based on the array type.
if (!B)
ExpandedBounds = CBD.ArrayExprBounds(Base);
else {
Base = CBD.CreateImplicitCast(Context.getDecayedType(Base->getType()),
CastKind::CK_ArrayToPointerDecay,
Base);
ExpandedBounds = CBD.ExpandToRange(Base, const_cast<BoundsExpr *>(B));
}
} else
ExpandedBounds = CBD.ExpandToRange(const_cast<VarDecl *>(D),
const_cast<BoundsExpr *>(B));
}
return CBD.ExpandToRange(const_cast<VarDecl *>(D),
const_cast<BoundsExpr *>(B));

// Attach the normalized bounds to D to avoid recomputing them.
D->setNormalizedBounds(ExpandedBounds);
return ExpandedBounds;
}

// Returns the declared bounds for the lvalue expression E. Assignments
Expand Down
48 changes: 48 additions & 0 deletions clang/test/CheckedC/inferred-bounds/bounds-context.c
Original file line number Diff line number Diff line change
Expand Up @@ -2766,3 +2766,51 @@ void conditional4(array_ptr<int> large : count(3),
// CHECK-NEXT: IntegerLiteral {{.*}} 1
// CHECK-NEXT: }
}

//////////////////////////////////////////////////////////////////////////
// Invalid variable declarations are not included in the bounds context //
//////////////////////////////////////////////////////////////////////////

// Parameter with an invalid declaration
_Checked void invalid_decl1(int **p : count(i), int i) { // expected-error {{parameter in a checked scope must have a bounds-safe interface type that uses only checked types or parameter/return types with bounds-safe interfaces}} \
// expected-note {{'int *' is not allowed in a checked scope}}
// Even though i is used in the declared bounds of p, since p is an invalid
// declaration, we do not include p in the observed bounds context, so this
// statement does not invalidate any bounds.
// Observed bounds context: { }
i = 0;
// CHECK: Statement S:
// CHECK-NEXT: BinaryOperator {{.*}} '='
// CHECK-NEXT: DeclRefExpr {{.*}} 'i'
// CHECK-NEXT: IntegerLiteral {{.*}} 0
// CHECK-NEXT: Observed bounds context after checking S:
// CHECK-NEXT: { }
}

// Local variable with an invalid declaration
_Checked void invalid_decl2(int i) {
// p is an invalid declaration, so we do not include p in the bounds context.
// Observed bounds context: { }
_Array_ptr<char *> p : count(i) = 0; // expected-error {{local variable in a checked scope must have a pointer, array or function type that uses only checked types or parameter/return types with bounds-safe interfaces}} \
// expected-note {{'char *' is not allowed in a checked scope}}
// CHECK: Statement S:
// CHECK-NEXT: DeclStmt
// CHECK-NEXT: VarDecl {{.*}} invalid p
// CHECK-NEXT: CountBoundsExpr {{.*}} Element
// CHECK-NEXT: ImplicitCastExpr {{.*}} <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'i'
// CHECK-NEXT: Observed bounds context after checking S:
// CHECK-NEXT: { }

// Even though i is used in the declared bounds of p, since p is an invalid
// declaration, we do not include p in the observed bounds context, so this
// statement does not invalidate any bounds.
// Observed bounds context: { }
i = 0;
// CHECK: Statement S:
// CHECK-NEXT: BinaryOperator {{.*}} '='
// CHECK-NEXT: DeclRefExpr {{.*}} 'i'
// CHECK-NEXT: IntegerLiteral {{.*}} 0
// CHECK-NEXT: Observed bounds context after checking S:
// CHECK-NEXT: { }
}