Skip to content
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -10565,6 +10565,10 @@ class Sema {
/// Returns a null QualType if there isn't one.
QualType GetCheckedCInteropType(ExprResult LHS);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need a clarifying comment? Or to be renamed?


/// \brief Get the bounds-safe interface type for RHS.
/// Returns a null QualType if there isn't one.
QualType GetCheckedCRValueInteropType(ExprResult RHS);

/// \brief If T is an array type, create a checked array type version of T.
/// This includes propagating the checked property to nested array types. If
/// a valid checked array type cannot be constructed and Diagnose is true,
Expand Down
65 changes: 65 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8479,6 +8479,18 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType();
RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType();

// If the LHS is a checked nt array type and the RHS is an unchecked
// nt array type with a bounds-safe interface, use the bounds-safe
// interface of the RHS to check the types.
if ((LHSType->isNtCheckedArrayType() ||

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the test isNtCheckedArrayType needed? I don't think C allows you to make assignments to values with array type.

LHSType->isCheckedPointerNtArrayType()) &&
(RHSType->isUncheckedPointerType() ||
RHSType->isUncheckedArrayType())) {
QualType RHSInteropType = GetCheckedCRValueInteropType(RHS);
if (!RHSInteropType.isNull())
RHSType = RHSInteropType;
}

// Common case: no conversion required.
if (LHSType == RHSType) {
Kind = CK_NoOp;
Expand Down Expand Up @@ -9116,6 +9128,22 @@ QualType Sema::GetCheckedCInteropType(ExprResult LHS) {
IsParam = isa<ParmVarDecl>(Var);
}
}
// If `e` has bounds-safe interface T* (or ptr<T>, etc.), then `*e` has
// bounds-safe interface T.
else if (UnaryOperator *Unary = dyn_cast<UnaryOperator>(LHSExpr)) {
if (Unary->getOpcode() == UnaryOperatorKind::UO_Deref) {
QualType T = GetCheckedCRValueInteropType(Unary->getSubExpr());
if (!T.isNull() && T->isPointerType())
return T->getPointeeType();
}
}
// If `e2` is an integer and `e1` has bounds-safe interface T* (or ptr<T>,
// etc.), then `e1[e2]` and `e2[e1]` have bounds-safe interface T.
else if (ArraySubscriptExpr *Array = dyn_cast<ArraySubscriptExpr>(LHSExpr)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the same change applies here.

QualType T = GetCheckedCRValueInteropType(Array->getBase());
if (!T.isNull() && T->isPointerType())
return T->getPointeeType();
}
}

if (D)
Expand All @@ -9124,6 +9152,43 @@ QualType Sema::GetCheckedCInteropType(ExprResult LHS) {
return QualType();
}

/// Get the bounds-safe interface type for an rvalue expression, if the
/// rvalue expression has a bounds-safe interface. Return a null QualType
/// otherwise. The rvalue expression may appear as part of the left-hand
/// side of an assignment - for example, as the subexpression of a pointer
/// deference or an array subscript. For rvalue expressions appearing as
/// part of the left-hand side of an assignment, only lvalue-to-rvalue casts
/// and pointer arithmetic have bounds-safe interfaces.
QualType Sema::GetCheckedCRValueInteropType(ExprResult RHS) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to returning the bounds-safe interface type for *e and p +/- i, should this method also return the bounds-safe interface type for &e? If e has bounds-safe interface type T, should &e have bounds-safe interface type ptr<T>?

As an example, making this change would allow the following to compile:

void address_of(int *interop_ptr : itype(ptr<int>), ptr<int> checked_ptr) {
  *(&interop_ptr) = checked_ptr;
}

Currently, this example does not compile since &interop_ptr has an unchecked pointer type int **. If GetCheckedCRValueInteropType were to return the bounds-safe interface type for address-of expressions, then &interop_ptr would have a bounds-safe interface type of ptr<ptr<int>> and so *(&interop_ptr) would have a bounds-safe interface type of ptr<int>.

if (!RHS.isInvalid()) {
Expr *RHSExpr = RHS.get()->IgnoreParens();
// If `e` has bounds-safe interface T, then `LValueToRValue(e)` has
// bounds-safe interface T.
if (CastExpr *Cast = dyn_cast<CastExpr>(RHSExpr)) {
if (Cast->getCastKind() == CastKind::CK_LValueToRValue) {
QualType T = GetCheckedCInteropType(Cast->getSubExpr());
return T;
}
}
// If `p` has bounds-safe interface T, then `p +/- i` has bounds-safe
// interface T, where `p` is a pointer and `i` is an integer.
else if (BinaryOperator *Binary = dyn_cast<BinaryOperator>(RHSExpr)) {
if (BinaryOperator::isAdditiveOp(Binary->getOpcode())) {
Expr *Left = Binary->getLHS();
Expr *Right = Binary->getRHS();
if (Left->getType()->isPointerType() &&
Right->getType()->isIntegerType())
return GetCheckedCRValueInteropType(Left);
else if (Right->getType()->isPointerType() &&
Left->getType()->isIntegerType())
return GetCheckedCRValueInteropType(Right);
}
}
}

return QualType();
}

QualType Sema::InvalidOperands(SourceLocation Loc, ExprResult &LHS,
ExprResult &RHS) {
OriginalOperand OrigLHS(LHS.get()), OrigRHS(RHS.get());
Expand Down