Skip to content

nonblocking/nonallocating attributes (was: nolock/noalloc) #84983

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 84 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
0619e30
Initial squash/cleanup
Mar 12, 2024
2a1b0d2
Cleanup, mostly code style
Mar 12, 2024
8aaeeb2
squash me
Mar 12, 2024
eb204df
apply the in-tree clang-format (which seems subtly different from my …
Mar 12, 2024
3b0277b
Address most of the low-hanging fruit in the initial review feedback.
Mar 13, 2024
4c6486f
Update clang/include/clang/AST/Type.h
dougsonos Mar 14, 2024
e6b1e35
- Sema::CheckAddCallableWithEffects: really do skip functions in depe…
Mar 13, 2024
fc67743
ASTContext::mergeFunctionTypes doesn't seem to need to do anything (?)
Mar 15, 2024
a638125
Fix function pointer type conversion in C by making IsFunctionConvers…
Mar 15, 2024
c2ba529
De-virtualize FunctionEffect; it's now a value type. Prepare for the …
Mar 16, 2024
2a5c14f
Remove unused parameters from FunctionEffect interface.
Mar 16, 2024
ad39599
Don't merge effects back to Old on redeclaration
Mar 16, 2024
513c88c
Fix: canInferOnFunction can't always get a TypeSourceInfo.
Mar 17, 2024
1325429
Cleanup of redeclarations (Sema::MergeFunctionDecl) so that the type …
Mar 19, 2024
5ac7429
FunctionEffectSet::create() is now a method of ASTContext; the global…
Mar 19, 2024
81b86df
- Serialize FunctionEffectSet as part of a FunctionProtoType.
Mar 19, 2024
8414186
Don't repeat function names in diagnostics.
Mar 20, 2024
62b125e
Functionality:
Mar 20, 2024
b15b53a
Merge remote-tracking branch 'llvm-origin/main' into nolock
Apr 3, 2024
525962e
Mass renaming: nolock/noalloc -> nonblocking/nonallocating
Apr 3, 2024
c026355
Remove vestigial FunctionEffect flags
Apr 3, 2024
02a3b7a
ArrayRef<const FunctionEffect> -> ArrayRef<FunctionEffect> (it's alre…
Apr 4, 2024
8121422
Adding the `blocking` and `allocating` attributes, as synonyms for `n…
Apr 5, 2024
9471aa1
Look for blocking and allocating attributes across all redeclarations.
Apr 5, 2024
12e213a
Reinstate the warning that nonblocking/nonallocating should be accomp…
Apr 5, 2024
2a58dda
Groundwork for `nonblocking(expr)`
Apr 9, 2024
e942a90
Low-hanging fruit in review feedback.
Apr 10, 2024
45389bd
- During parsing, don't remove nonallocating when nonblocking is adde…
Apr 11, 2024
80d5e7a
- Get rid of ugly const_cast in Sema::CheckOverridingFunctionAttributes.
Apr 11, 2024
9e223a3
Split out the Objective-C tests into a separate source file.
Apr 11, 2024
9bac8d2
Make FunctionEffectSet::operator==() more elegant.
Apr 11, 2024
b955435
Create a flat effect representation in FunctionProtoType. Renaming of…
Apr 13, 2024
6045573
Separate effects and conditions.
Apr 15, 2024
57d09ce
FunctionEffectCondExpr => FunctionEffectCondition
Apr 16, 2024
a9edec6
FunctionTypeEffectSet => FunctionEffectSet
Apr 16, 2024
80495b7
nonblocking(cond) now parsed into new form.
Apr 16, 2024
1853b39
wip with computed effects
Apr 18, 2024
0aa20af
- Inline the methods on Decl.
Apr 23, 2024
67c3e47
Merge llvm-origin/main into nolock
Apr 23, 2024
ca9b8fa
- Adapt to merge from main.
Apr 24, 2024
5143877
tiny cleanup
Apr 24, 2024
8c691dc
Merge main into nolock
Apr 25, 2024
65e5b61
Tests involving nonblocking(expression)
Apr 25, 2024
1748edb
ASTReader: adapt to GlobalDeclID change
Apr 26, 2024
485c941
warn_perf_constraint_implies_noexcept should only be issued in C++ wi…
Apr 26, 2024
92e4b9a
Obtain extern-C-ness from the function's canonical Decl.
Apr 28, 2024
c534d63
Fix build problem involving std::any_of and a FunctionEffectsRef.
Apr 29, 2024
5cd7bfd
Remove AnalysisBasedWarnings debug code that was pulling in ASTMatchers.
May 2, 2024
c18b774
Remove the 2nd pass caller/callee analysis/verification for now (to b…
May 2, 2024
13e71c0
Merge branch 'main' into nolock
May 2, 2024
b6cfea8
In Sema::ImpCastExprToType, the check for a nullptr source was
May 2, 2024
711bc56
Better attribute documentation
May 2, 2024
990f5bb
- Remove disabling of future warning
May 3, 2024
db576ef
Cleanup dead code.
May 3, 2024
b95964c
- AttrDocs: add note about -W option to control diagnostics.
May 3, 2024
46830f6
Review feedback, notably: FunctionEffectsRef invariants, mergeFunctio…
May 4, 2024
d918424
Review feedback:
May 4, 2024
86795b4
Detect and diagnose conflicts when merging effects between declarations.
May 5, 2024
8fdea5b
In CheckOverridingFunctionAttributes, gather and report effect confli…
May 6, 2024
f4e299f
nonblocking(expr): Simplify ActOnEffectExpression, add tests.
May 6, 2024
cad5d0d
- Require function prototypes in conjunction with the new attributes.
May 6, 2024
f4112ff
tighten up ActOnEffectExpression
May 6, 2024
c2848f8
More syntax tests.
May 7, 2024
1fef4b1
Apply suggestions from code review
dougsonos May 16, 2024
ba7f53f
- review feedback: getCondition(), default-constructable iterator.
May 16, 2024
173d36b
Remove the "clang_" prefix from the attribute spellings.
May 16, 2024
5438e96
More review feedback.
May 16, 2024
9e4782c
- Clean up comments (trailing period).
May 17, 2024
60bf34c
TreeTransform is a class not a struct.
May 21, 2024
13ba987
Merge branch 'main' into nolock
May 21, 2024
877dec8
Add a positive test case per Aaron's feedback
May 22, 2024
6a92ddf
Merge branch 'main' into nolock
May 22, 2024
8bef15b
Fix: read past end of Conditions array in FunctionEffectSet::insert.
May 24, 2024
f015a24
Apply suggestions from code review
dougsonos May 24, 2024
d8d34be
FunctionEffectCondition => EffectConditionExpr, more minor review fee…
May 24, 2024
29d2c3d
In FunctionEffectsRef::get(QualType QT), handle case of multiple levels
May 24, 2024
8204611
Merge llvm-origin/main into nolock
May 27, 2024
3ac9bcb
Review feedback:
May 27, 2024
dbc511a
Add tests suggested by Aaron Ballman.
Jun 18, 2024
7cda574
Defer diagnosis of conflicting effect attributes when there is a cond…
Jun 20, 2024
23ff5a5
tolerate/ignore redundant attributes
Jun 20, 2024
23d486b
Merge remote-tracking branch 'llvm-origin/main' into nolock
Jun 20, 2024
7505858
Release notes
May 3, 2024
64fede2
fix test failure (last merge from main changed how bool template argu…
Jun 21, 2024
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
9 changes: 9 additions & 0 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3008,6 +3008,13 @@ class FunctionDecl : public DeclaratorDecl,
/// computed and stored.
unsigned getODRHash() const;

FunctionEffectSet getFunctionEffects() const {
const auto *FPT = getType()->getAs<FunctionProtoType>();
if (FPT)
return FPT->getFunctionEffects();
return {};
}

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) {
Expand Down Expand Up @@ -4633,6 +4640,8 @@ class BlockDecl : public Decl, public DeclContext {

SourceRange getSourceRange() const override LLVM_READONLY;

FunctionEffectSet getFunctionEffects() const;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == Block; }
Expand Down
300 changes: 295 additions & 5 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -4047,8 +4047,12 @@ class FunctionType : public Type {
LLVM_PREFERRED_TYPE(bool)
unsigned HasArmTypeAttributes : 1;

LLVM_PREFERRED_TYPE(bool)
unsigned HasFunctionEffects : 1;

FunctionTypeExtraBitfields()
: NumExceptionType(0), HasArmTypeAttributes(false) {}
: NumExceptionType(0), HasArmTypeAttributes(false),
HasFunctionEffects(false) {}
};

/// The AArch64 SME ACLE (Arm C/C++ Language Extensions) define a number
Expand Down Expand Up @@ -4181,6 +4185,131 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode {
}
};

class FunctionEffect;
class FunctionEffectSet;

// It is the user's responsibility to keep this in set form: elements are
// ordered and unique.
// We could hide the mutating methods which are capable of breaking the
// invariant, but they're needed and safe when used with STL set algorithms.
class MutableFunctionEffectSet : public SmallVector<const FunctionEffect *, 4> {
public:
using SmallVector::insert;
using SmallVector::SmallVector;

/// Maintains order/uniquenesss.
void insert(const FunctionEffect *effect);

MutableFunctionEffectSet &operator|=(FunctionEffectSet rhs);
};

class FunctionEffectSet {
public:
// These sets will tend to be very small (1 element), so represent them as
// sorted vectors, which are compatible with the STL set algorithms. Using an
// array or vector also means the elements are contiguous, keeping iterators
// simple.

private:
// 'Uniqued' refers to the set itself being uniqued. Storage is allocated
// separately. Use ArrayRef for its iterators. Subclass so as to be able to
// compare (it seems ArrayRef would silently convert itself to a vector for
// comparison?!).
class UniquedAndSortedFX : public llvm::ArrayRef<const FunctionEffect *> {
public:
using Base = llvm::ArrayRef<const FunctionEffect *>;

UniquedAndSortedFX(Base Array) : Base(Array) {}
UniquedAndSortedFX(const FunctionEffect **Ptr, size_t Len)
: Base(Ptr, Len) {}

bool operator<(const UniquedAndSortedFX &rhs) const;
};

// Could have used a TinyPtrVector if it were unique-able.
// Empty set has a null Impl.
llvm::PointerUnion<const FunctionEffect *, const UniquedAndSortedFX *> Impl;

explicit FunctionEffectSet(const FunctionEffect *Single) : Impl(Single) {}
explicit FunctionEffectSet(const UniquedAndSortedFX *Multi) : Impl(Multi) {}

public:
using Differences =
SmallVector<std::pair<const FunctionEffect *, bool /*added*/>>;

FunctionEffectSet() : Impl(nullptr) {}

void *getOpaqueValue() const { return Impl.getOpaqueValue(); }

explicit operator bool() const { return !empty(); }
bool empty() const {
if (Impl.isNull())
return true;
if (const UniquedAndSortedFX *Vec =
dyn_cast_if_present<const UniquedAndSortedFX *>(Impl))
return Vec->empty();
return false;
}
size_t size() const {
if (empty())
return 0;
if (isa<const FunctionEffect *>(Impl))
return 1;
return cast<const UniquedAndSortedFX *>(Impl)->size();
}

using iterator = const FunctionEffect *const *;

iterator begin() const {
if (isa<const FunctionEffect *>(Impl))
return Impl.getAddrOfPtr1();
return cast<const UniquedAndSortedFX *>(Impl)->begin();
}

iterator end() const {
if (isa<const FunctionEffect *>(Impl))
return begin() + (Impl.isNull() ? 0 : 1);
return cast<const UniquedAndSortedFX *>(Impl)->end();
}

ArrayRef<const FunctionEffect *> items() const { return {begin(), end()}; }

// Since iterators are non-trivial and sets are very often empty,
// encourage short-circuiting loops for the empty set.
// void for_each(llvm::function_ref<void(const FunctionEffect*)> func) const;

bool operator==(const FunctionEffectSet &other) const {
return Impl == other.Impl;
}
bool operator!=(const FunctionEffectSet &other) const {
return Impl != other.Impl;
}
bool operator<(const FunctionEffectSet &other) const;

void dump(llvm::raw_ostream &OS) const;

/// Factory functions: return instances with uniqued implementations.
static FunctionEffectSet create(const FunctionEffect &single) {
return FunctionEffectSet{&single};
}
static FunctionEffectSet create(llvm::ArrayRef<const FunctionEffect *> items);

/// Union. Caller should check for incompatible effects.
FunctionEffectSet operator|(const FunctionEffectSet &rhs) const;
/// Intersection.
MutableFunctionEffectSet operator&(const FunctionEffectSet &rhs) const;
/// Difference.
MutableFunctionEffectSet operator-(const FunctionEffectSet &rhs) const;

/// Caller should short-circuit by checking for equality first.
static Differences differences(const FunctionEffectSet &Old,
const FunctionEffectSet &New);

/// Extract the effects from a Type if it is a BlockType or FunctionProtoType,
/// or pointer to one.
static FunctionEffectSet get(const Type &TyRef);
};

/// Represents a prototype with parameter type info, e.g.
/// 'int foo(int)' or 'int foo(void)'. 'void' is represented as having no
/// parameters, not as having a single void parameter. Such a type can have
Expand All @@ -4195,7 +4324,8 @@ class FunctionProtoType final
FunctionProtoType, QualType, SourceLocation,
FunctionType::FunctionTypeExtraBitfields,
FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType,
Expr *, FunctionDecl *, FunctionType::ExtParameterInfo, Qualifiers> {
Expr *, FunctionDecl *, FunctionType::ExtParameterInfo,
FunctionEffectSet, Qualifiers> {
friend class ASTContext; // ASTContext creates these.
friend TrailingObjects;

Expand Down Expand Up @@ -4226,9 +4356,12 @@ class FunctionProtoType final
// an ExtParameterInfo for each of the parameters. Present if and
// only if hasExtParameterInfos() is true.
//
// * Optionally, a FunctionEffectSet. Present if and only if
// hasFunctionEffects() is true.
//
// * Optionally a Qualifiers object to represent extra qualifiers that can't
// be represented by FunctionTypeBitfields.FastTypeQuals. Present if and only
// if hasExtQualifiers() is true.
// be represented by FunctionTypeBitfields.FastTypeQuals. Present if and
// only if hasExtQualifiers() is true.
//
// The optional FunctionTypeExtraBitfields has to be before the data
// related to the exception specification since it contains the number
Expand Down Expand Up @@ -4284,6 +4417,7 @@ class FunctionProtoType final
ExceptionSpecInfo ExceptionSpec;
const ExtParameterInfo *ExtParameterInfos = nullptr;
SourceLocation EllipsisLoc;
FunctionEffectSet FunctionEffects;

ExtProtoInfo()
: Variadic(false), HasTrailingReturn(false),
Expand All @@ -4301,7 +4435,7 @@ class FunctionProtoType final

bool requiresFunctionProtoTypeExtraBitfields() const {
return ExceptionSpec.Type == EST_Dynamic ||
requiresFunctionProtoTypeArmAttributes();
requiresFunctionProtoTypeArmAttributes() || FunctionEffects;
}

bool requiresFunctionProtoTypeArmAttributes() const {
Expand Down Expand Up @@ -4349,6 +4483,10 @@ class FunctionProtoType final
return hasExtParameterInfos() ? getNumParams() : 0;
}

unsigned numTrailingObjects(OverloadToken<FunctionEffectSet>) const {
return hasFunctionEffects();
}

/// Determine whether there are any argument types that
/// contain an unexpanded parameter pack.
static bool containsAnyUnexpandedParameterPack(const QualType *ArgArray,
Expand Down Expand Up @@ -4450,6 +4588,7 @@ class FunctionProtoType final
EPI.RefQualifier = getRefQualifier();
EPI.ExtParameterInfos = getExtParameterInfosOrNull();
EPI.AArch64SMEAttributes = getAArch64SMEAttributes();
EPI.FunctionEffects = getFunctionEffects();
return EPI;
}

Expand Down Expand Up @@ -4661,6 +4800,18 @@ class FunctionProtoType final
return false;
}

bool hasFunctionEffects() const {
if (!hasExtraBitfields())
return false;
return getTrailingObjects<FunctionTypeExtraBitfields>()->HasFunctionEffects;
}

FunctionEffectSet getFunctionEffects() const {
if (hasFunctionEffects())
return *getTrailingObjects<FunctionEffectSet>();
return {};
}

bool isSugared() const { return false; }
QualType desugar() const { return QualType(this, 0); }

Expand Down Expand Up @@ -7754,6 +7905,145 @@ QualType DecayedType::getPointeeType() const {
void FixedPointValueToString(SmallVectorImpl<char> &Str, llvm::APSInt Val,
unsigned Scale);

// ------------------------------------------------------------------------------

// TODO: Should FunctionEffect be located elsewhere, where Decl is not
// forward-declared?
class Decl;
class CXXMethodDecl;

/// Represents an abstract function effect.
class FunctionEffect {
public:
enum EffectType {
kGeneric,
kNoLockTrue,
kNoAllocTrue,
};

/// Flags describing behaviors of the effect.
using Flags = unsigned;
enum FlagBit : unsigned {
// Some effects require verification, e.g. nolock(true); others might not?
// (no example yet)
kRequiresVerification = 0x1,

// Does this effect want to verify all function calls originating in
// functions having this effect?
kVerifyCalls = 0x2,

// Can verification inspect callees' implementations? (e.g. nolock: yes,
// tcb+types: no)
kInferrableOnCallees = 0x4,

// Language constructs which effects can diagnose as disallowed.
kExcludeThrow = 0x8,
kExcludeCatch = 0x10,
kExcludeObjCMessageSend = 0x20,
kExcludeStaticLocalVars = 0x40,
kExcludeThreadLocalVars = 0x80
};

private:
const EffectType Type_;
const Flags Flags_;
const char *Name;

public:
using CalleeDeclOrType =
llvm::PointerUnion<const Decl *, const FunctionProtoType *>;

FunctionEffect(EffectType T, Flags F, const char *Name)
: Type_(T), Flags_(F), Name(Name) {}
virtual ~FunctionEffect();

/// The type of the effect.
EffectType type() const { return Type_; }

/// Flags describing behaviors of the effect.
Flags flags() const { return Flags_; }

/// The description printed in diagnostics, e.g. 'nolock'.
StringRef name() const { return Name; }

/// The description used by TypePrinter, e.g. __attribute__((clang_nolock))
virtual std::string attribute() const = 0;

/// Return true if adding or removing the effect as part of a type conversion
/// should generate a diagnostic.
virtual bool diagnoseConversion(bool Adding, QualType OldType,
FunctionEffectSet OldFX, QualType NewType,
FunctionEffectSet NewFX) const;

/// Return true if adding or removing the effect in a redeclaration should
/// generate a diagnostic.
virtual bool diagnoseRedeclaration(bool Adding,
const FunctionDecl &OldFunction,
FunctionEffectSet OldFX,
const FunctionDecl &NewFunction,
FunctionEffectSet NewFX) const;

/// Return true if adding or removing the effect in a C++ virtual method
/// override should generate a diagnostic.
virtual bool diagnoseMethodOverride(bool Adding,
const CXXMethodDecl &OldMethod,
FunctionEffectSet OldFX,
const CXXMethodDecl &NewMethod,
FunctionEffectSet NewFX) const;

/// Return true if the effect is allowed to be inferred on the specified Decl
/// (may be a FunctionDecl or BlockDecl). Only used if the effect has
/// kInferrableOnCallees flag set. Example: This allows nolock(false) to
/// prevent inference for the function.
virtual bool canInferOnDecl(const Decl *Caller,
FunctionEffectSet CallerFX) const;

// Called if kVerifyCalls flag is set; return false for success. When true is
// returned for a direct call, then the kInferrableOnCallees flag may trigger
// inference rather than an immediate diagnostic. Caller should be assumed to
// have the effect (it may not have it explicitly when inferring).
virtual bool diagnoseFunctionCall(bool Direct, const Decl *Caller,
FunctionEffectSet CallerFX,
CalleeDeclOrType Callee,
FunctionEffectSet CalleeFX) const;
};

/// FunctionEffect subclass for nolock and noalloc (whose behaviors are close
/// to identical).
class NoLockNoAllocEffect : public FunctionEffect {
bool isNoLock() const { return type() == kNoLockTrue; }

public:
static const NoLockNoAllocEffect &nolock_instance();
static const NoLockNoAllocEffect &noalloc_instance();

NoLockNoAllocEffect(EffectType Type, const char *Name);
~NoLockNoAllocEffect() override;

std::string attribute() const override;

bool diagnoseConversion(bool Adding, QualType OldType,
FunctionEffectSet OldFX, QualType NewType,
FunctionEffectSet NewFX) const override;

bool diagnoseRedeclaration(bool Adding, const FunctionDecl &OldFunction,
FunctionEffectSet OldFX,
const FunctionDecl &NewFunction,
FunctionEffectSet NewFX) const override;

bool diagnoseMethodOverride(bool Adding, const CXXMethodDecl &OldMethod,
FunctionEffectSet OldFX,
const CXXMethodDecl &NewMethod,
FunctionEffectSet NewFX) const override;

bool canInferOnDecl(const Decl *Caller,
FunctionEffectSet CallerFX) const override;

bool diagnoseFunctionCall(bool Direct, const Decl *Caller,
FunctionEffectSet CallerFX, CalleeDeclOrType Callee,
FunctionEffectSet CalleeFX) const override;
};

} // namespace clang

#endif // LLVM_CLANG_AST_TYPE_H
Loading