Skip to content

Commit f680d86

Browse files
committed
[BoundsSafety] Delay processing of bounds attrs in templates
Dynamic bounds attributes do not handle value dependent arguments. To enable wider interop with C++ code bases we delay the processing of these attributes inside templated contexts. Instead we apply the new type while instantiating the function. One issue with this approach is that instantiation happens after parsing is finished, and the Scope information we use to prevent attributes from referring to arguments from outer scopes is only available during parsing. These diagnostics were emitted at the end of the type processing. In templated contexts we instead perform that analysis during parsing, and delay the rest of the processing. To avoid churn in unrelated tests the rest of the attributes keep their processing as is, until we've investigated what impact hoisting it would have on the user experience.
1 parent 257562b commit f680d86

File tree

8 files changed

+443
-36
lines changed

8 files changed

+443
-36
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15509,7 +15509,8 @@ class Sema final : public SemaBase {
1550915509
AttributeCommonInfo::Kind Kind,
1551015510
Expr *AttrArg, SourceLocation Loc,
1551115511
SourceRange Range, StringRef DiagName,
15512-
bool OriginatesInAPINotes = false);
15512+
bool OriginatesInAPINotes = false,
15513+
bool InInstantiatedTemplate = false);
1551315514

1551415515
/// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
1551515516
/// `__counted_by_or_null` pointer type \param LHSTy.

clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3484,6 +3484,9 @@ namespace clang {
34843484

34853485
void DynamicCountPointerAssignmentAnalysis::run() {
34863486
AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, dcl);
3487+
if (isa<TemplateDecl>(dcl)) // delay processing until template has been
3488+
// instantiated
3489+
return;
34873490

34883491
CFG *cfg = AC.getCFG();
34893492
if (!cfg)

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 150 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "DynamicCountPointerAssignmentAnalysis.h"
14+
#include "TreeTransform.h"
1315
#include "clang/AST/ASTConsumer.h"
1416
#include "clang/AST/ASTContext.h"
1517
#include "clang/AST/ASTMutationListener.h"
@@ -72,8 +74,6 @@
7274
#include "llvm/Support/MathExtras.h"
7375
#include "llvm/Support/raw_ostream.h"
7476
#include "llvm/TargetParser/Triple.h"
75-
#include "TreeTransform.h"
76-
#include "DynamicCountPointerAssignmentAnalysis.h"
7777
#include <optional>
7878

7979
using namespace clang;
@@ -6141,6 +6141,66 @@ static bool CheckArgLifetimeAndScope(
61416141
return Invalid;
61426142
}
61436143

6144+
static BoundsAttributedType::BoundsAttrKind
6145+
getBoundsAttrKind(AttributeCommonInfo::Kind Kind) {
6146+
switch (Kind) {
6147+
case ParsedAttr::AT_SizedBy:
6148+
return BoundsAttributedType::SizedBy;
6149+
case ParsedAttr::AT_SizedByOrNull:
6150+
return BoundsAttributedType::SizedByOrNull;
6151+
case ParsedAttr::AT_CountedBy:
6152+
return BoundsAttributedType::CountedBy;
6153+
case ParsedAttr::AT_CountedByOrNull:
6154+
return BoundsAttributedType::CountedByOrNull;
6155+
case ParsedAttr::AT_PtrEndedBy:
6156+
return BoundsAttributedType::EndedBy;
6157+
default:
6158+
llvm_unreachable("unexpected bounds attribute");
6159+
}
6160+
}
6161+
6162+
class EarlyLifetimeAndScopeCheck
6163+
: public RecursiveASTVisitor<EarlyLifetimeAndScopeCheck> {
6164+
Sema &SemaRef;
6165+
bool ScopeCheck;
6166+
Sema::LifetimeCheckKind LifetimeCheck;
6167+
BoundsAttributedType::BoundsAttrKind AttrKind;
6168+
bool IsArrayType;
6169+
bool HadError = false;
6170+
llvm::SmallPtrSet<Decl *, 16> Visited;
6171+
6172+
public:
6173+
EarlyLifetimeAndScopeCheck(Sema &S, bool SC, Sema::LifetimeCheckKind LC,
6174+
AttributeCommonInfo::Kind AK, bool IsArray)
6175+
: SemaRef(S), ScopeCheck(SC), LifetimeCheck(LC),
6176+
AttrKind(getBoundsAttrKind(AK)), IsArrayType(IsArray) {}
6177+
6178+
bool VisitDeclRefExpr(DeclRefExpr *E) {
6179+
ValueDecl *VD = E->getDecl();
6180+
bool IsNewVD = Visited.insert(VD).second;
6181+
if (IsNewVD) {
6182+
HadError |=
6183+
CheckArgLifetimeAndScope(SemaRef, VD, E->getExprLoc(), ScopeCheck,
6184+
LifetimeCheck, AttrKind, IsArrayType);
6185+
}
6186+
return true;
6187+
}
6188+
6189+
bool VisitMemberExpr(MemberExpr *E) {
6190+
TraverseStmt(E->getBase());
6191+
ValueDecl *VD = E->getMemberDecl();
6192+
bool IsNewVD = Visited.insert(VD).second;
6193+
if (IsNewVD) {
6194+
HadError |=
6195+
CheckArgLifetimeAndScope(SemaRef, VD, E->getExprLoc(), ScopeCheck,
6196+
LifetimeCheck, AttrKind, IsArrayType);
6197+
}
6198+
return true;
6199+
}
6200+
6201+
bool hadError() { return HadError; }
6202+
};
6203+
61446204
static bool diagnoseBoundsAttrLifetimeAndScope(
61456205
Sema &SemaRef, const BoundsAttributedType *Ty, bool ScopeCheck,
61466206
Sema::LifetimeCheckKind LifetimeCheck) {
@@ -6383,11 +6443,35 @@ diagnoseRangeDependentDecls(Sema &S, const ValueDecl *TheDepender,
63836443
return HadError;
63846444
}
63856445

6446+
class DynamicBoundsAttrInfo {
6447+
public:
6448+
QualType Ty;
6449+
unsigned EffectiveLevel;
6450+
bool IsFPtr;
6451+
6452+
DynamicBoundsAttrInfo(QualType DeclTy, unsigned Level) {
6453+
IsFPtr = false;
6454+
EffectiveLevel = Level;
6455+
Ty = DeclTy;
6456+
for (unsigned i = 0; i != Level; ++i) {
6457+
if (!Ty->isPointerType())
6458+
break;
6459+
Ty = Ty->getPointeeType();
6460+
if (Ty->isFunctionType()) {
6461+
IsFPtr = true;
6462+
EffectiveLevel = Level - i - 1;
6463+
break;
6464+
}
6465+
}
6466+
}
6467+
};
6468+
63866469
void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
63876470
AttributeCommonInfo::Kind Kind,
63886471
Expr *AttrArg, SourceLocation Loc,
63896472
SourceRange Range, StringRef DiagName,
6390-
bool OriginatesInAPINotes) {
6473+
bool OriginatesInAPINotes,
6474+
bool InInstantiatedTemplate) {
63916475
// If the decl is invalid, the indirection Level might not exist in the type,
63926476
// since the type may have not been constructed correctly. Example:
63936477
// 'int (*param)[__counted_by_or_null(10)][]'
@@ -6401,22 +6485,10 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
64016485
auto *Var = dyn_cast<VarDecl>(D);
64026486
QualType DeclTy = TND ? TND->getUnderlyingType() : VD->getType();
64036487

6404-
bool IsFPtr = false;
6405-
unsigned EffectiveLevel = Level;
6406-
QualType Ty = DeclTy;
6407-
for (unsigned i = 0; i != Level; ++i) {
6408-
if (!Ty->isPointerType())
6409-
break;
6410-
Ty = Ty->getPointeeType();
6411-
if (Ty->isFunctionType()) {
6412-
IsFPtr = true;
6413-
EffectiveLevel = Level - i - 1;
6414-
break;
6415-
}
6416-
}
6488+
DynamicBoundsAttrInfo Info(DeclTy, Level);
64176489

64186490
// Don't allow typedefs with __counted_by on non-function types.
6419-
if (TND && (!DeclTy->isFunctionType() && !IsFPtr)) {
6491+
if (TND && (!DeclTy->isFunctionType() && !Info.IsFPtr)) {
64206492
Diag(Loc, diag::err_bounds_safety_typedef_dynamic_bound) << DiagName;
64216493
return;
64226494
}
@@ -6490,7 +6562,7 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
64906562
return;
64916563
}
64926564

6493-
if (EffectiveLevel != 0 &&
6565+
if (Info.EffectiveLevel != 0 &&
64946566
(!isa<ParmVarDecl>(VD) || DeclTy->isBoundsAttributedType())) {
64956567
Diag(Loc, diag::err_bounds_safety_nested_dynamic_bound) << DiagName;
64966568
return;
@@ -6519,14 +6591,14 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
65196591
}
65206592
}
65216593

6522-
if (Ty->isArrayType() && OrNull &&
6523-
(FD || EffectiveLevel > 0 || (Var && Var->hasExternalStorage()))) {
6594+
if (Info.Ty->isArrayType() && OrNull &&
6595+
(FD || Info.EffectiveLevel > 0 || (Var && Var->hasExternalStorage()))) {
65246596
auto ErrDiag = Diag(Loc, diag::err_bounds_safety_nullable_fam);
65256597
// Pointers to dynamic count types are only allowed for parameters, so any
65266598
// FieldDecl containing a dynamic count type is a FAM. I.e. a struct field
65276599
// with type 'int(*)[__counted_by(...)]' is not valid.
65286600
ErrDiag << CountInBytes << /*is FAM?*/ !!FD << DiagName;
6529-
assert(!FD || EffectiveLevel == 0);
6601+
assert(!FD || Info.EffectiveLevel == 0);
65306602

65316603
SourceLocation FixItLoc = getSourceManager().getExpansionLoc(Loc);
65326604
SourceLocation EndLoc =
@@ -6538,19 +6610,19 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
65386610
return;
65396611
}
65406612

6541-
if (Ty->isArrayType() && EffectiveLevel > 0) {
6613+
if (Info.Ty->isArrayType() && Info.EffectiveLevel > 0) {
65426614
auto ErrDiag =
65436615
Diag(
65446616
Loc,
65456617
diag::
65466618
err_bounds_safety_unsupported_address_of_incomplete_array_type)
6547-
<< Ty;
6619+
<< Info.Ty;
65486620
// apply attribute anyways to avoid too misleading follow-up diagnostics
65496621
}
65506622
}
65516623

65526624
QualType NewDeclTy{};
6553-
bool ScopeCheck = (Var && Var->isLocalVarDecl()) || IsFPtr;
6625+
bool ScopeCheck = (Var && Var->isLocalVarDecl()) || Info.IsFPtr;
65546626
const BoundsAttributedType *ConstructedType = nullptr;
65556627

65566628
bool HadAtomicError = false;
@@ -6598,19 +6670,24 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
65986670
// nested type, so `ConstructedType` can be different from `NewDeclTy`.
65996671
assert(ConstructedType);
66006672

6601-
auto LifetimeCheck =
6602-
IsFPtr ? Sema::LifetimeCheckKind::None : Sema::getLifetimeCheckKind(Var);
6603-
if (diagnoseBoundsAttrLifetimeAndScope(*this, ConstructedType, ScopeCheck,
6673+
auto LifetimeCheck = Info.IsFPtr ? Sema::LifetimeCheckKind::None
6674+
: Sema::getLifetimeCheckKind(Var);
6675+
// Scope information is not available after template instantiation, so this
6676+
// check has been performed earlier if this is a template instantiation.
6677+
if (!InInstantiatedTemplate &&
6678+
diagnoseBoundsAttrLifetimeAndScope(*this, ConstructedType, ScopeCheck,
66046679
LifetimeCheck))
66056680
return;
66066681

66076682
if (VD && !isa<FunctionDecl>(VD) && !HadAtomicError) {
66086683
if (const auto *BDTy = dyn_cast<CountAttributedType>(ConstructedType)) {
6609-
if (!diagnoseCountDependentDecls(*this, VD, BDTy, EffectiveLevel, IsFPtr))
6610-
AttachDependerDeclsAttr(VD, BDTy, EffectiveLevel);
6684+
if (!diagnoseCountDependentDecls(*this, VD, BDTy, Info.EffectiveLevel,
6685+
Info.IsFPtr))
6686+
AttachDependerDeclsAttr(VD, BDTy, Info.EffectiveLevel);
66116687
} else if (const auto *BDTy =
66126688
dyn_cast<DynamicRangePointerType>(ConstructedType)) {
6613-
diagnoseRangeDependentDecls(*this, VD, BDTy, EffectiveLevel, IsFPtr);
6689+
diagnoseRangeDependentDecls(*this, VD, BDTy, Info.EffectiveLevel,
6690+
Info.IsFPtr);
66146691
} else {
66156692
llvm_unreachable("Unexpected bounds attributed type");
66166693
}
@@ -6635,11 +6712,54 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
66356712
}
66366713
}
66376714

6715+
static void attachLateInstantiatedCountedByEndedByAttr(Sema &S, Decl *D,
6716+
const ParsedAttr &AL,
6717+
int Level) {
6718+
#define ADD_ATTR(Kind) \
6719+
case ParsedAttr::AT_##Kind: \
6720+
D->addAttr(::new (S.Context) \
6721+
Kind##Attr(S.Context, AL, AL.getArgAsExpr(0), Level)); \
6722+
break;
6723+
6724+
switch (AL.getKind()) {
6725+
ADD_ATTR(SizedBy)
6726+
ADD_ATTR(SizedByOrNull)
6727+
ADD_ATTR(CountedBy)
6728+
ADD_ATTR(CountedByOrNull)
6729+
ADD_ATTR(PtrEndedBy)
6730+
default:
6731+
llvm_unreachable("Invalid dynamic bound attribute");
6732+
}
6733+
#undef ADD_ATTR
6734+
}
6735+
66386736
static void handlePtrCountedByEndedByAttr(Sema &S, Decl *D,
66396737
const ParsedAttr &AL) {
66406738
unsigned Level;
66416739
if (!S.checkUInt32Argument(AL, AL.getArgAsExpr(1), Level))
66426740
return;
6741+
6742+
if (D->getDescribedTemplate() || S.CurContext->isDependentContext()) {
6743+
auto *TND = dyn_cast<TypedefNameDecl>(D);
6744+
auto *VD = dyn_cast<ValueDecl>(D);
6745+
auto *Var = dyn_cast<VarDecl>(D);
6746+
QualType DeclTy = TND ? TND->getUnderlyingType() : VD->getType();
6747+
DynamicBoundsAttrInfo Info(DeclTy, Level);
6748+
// Scope information will be invalid by the time we instantiate the
6749+
// template, so perform these checks now and delay the rest of the
6750+
// processing until the point of instantiation.
6751+
auto LifetimeCheck = Info.IsFPtr ? Sema::LifetimeCheckKind::None
6752+
: Sema::getLifetimeCheckKind(Var);
6753+
bool ScopeCheck = (Var && Var->isLocalVarDecl()) || Info.IsFPtr;
6754+
EarlyLifetimeAndScopeCheck EarlyCheck(S, ScopeCheck, LifetimeCheck,
6755+
AL.getKind(), Info.Ty->isArrayType());
6756+
EarlyCheck.TraverseStmt(AL.getArgAsExpr(0));
6757+
if (EarlyCheck.hadError())
6758+
return;
6759+
attachLateInstantiatedCountedByEndedByAttr(S, D, AL, Level);
6760+
return;
6761+
}
6762+
66436763
AttributeCommonInfo::Kind Kind = AL.getKind();
66446764
const IdentifierInfo *AttrName =
66456765
AL.printAsMacro() ? AL.getMacroIdentifier() : AL.getAttrName();

clang/lib/Sema/SemaExpr.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "DynamicCountPointerAssignmentAnalysis.h"
1413
#include "CheckExprLifetime.h"
14+
#include "DynamicCountPointerAssignmentAnalysis.h"
1515
#include "TreeTransform.h"
1616
#include "UsedDeclVisitor.h"
1717
#include "clang/AST/ASTConsumer.h"
@@ -1012,7 +1012,7 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E,
10121012
// BoundsSafety: rvalue of pointers with dynamic count or range will
10131013
// automatically form wide pointers.
10141014
auto *DBPT = T->getAs<BoundsAttributedType>();
1015-
if (getLangOpts().BoundsSafety && DBPT) {
1015+
if (getLangOpts().BoundsSafety && DBPT && !CurContext->isDependentContext()) {
10161016
CopyExpr Copy(*this);
10171017
SmallVector<OpaqueValueExpr *, 2> OpaqueValues;
10181018
// if the lvalue was a member access, it's possible its base expression
@@ -7858,7 +7858,7 @@ bool Sema::CheckDynamicCountSizeForAssignment(
78587858

78597859
// The count expression might be valid, but the new value assigned may not
78607860
// be, so check again after transforming
7861-
if (CountExpr->isValueDependent())
7861+
if (CountExpr->containsErrors())
78627862
return false;
78637863
}
78647864
const auto &ReplacingValues = Transform.GetReplacingValues();
@@ -8819,7 +8819,8 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
88198819
}
88208820

88218821
/* TO_UPSTREAM(BoundsSafety) ON*/
8822-
if (getLangOpts().BoundsSafetyAttributes && FDecl) {
8822+
if (getLangOpts().BoundsSafetyAttributes && FDecl &&
8823+
!CurContext->isDependentContext()) {
88238824
// FIXME: We need to support function pointers and blocks that don't have
88248825
// function decl.
88258826
if (!checkDynamicCountPointerAsParameter(*this, FDecl, TheCall))
@@ -25073,6 +25074,10 @@ ExprResult Sema::ActOnBoundsSafetyCall(ExprResult Call) {
2507325074
if (!CallE || CallE->containsErrors())
2507425075
return Call;
2507525076

25077+
// Only process instantiated versions of templates
25078+
if (CurContext->isDependentContext())
25079+
return Call;
25080+
2507625081
// Bail out early if there's nothing to do. (There is something to do if
2507725082
// there's a dynamic bound pointer type in this function prototype, or if
2507825083
// it's annotated with `alloc_size`, in which case we can derive the

0 commit comments

Comments
 (0)