Thread `TypeLoc` through `FunctionLifetimes` and `ValueLifetimes`.
A followup CL will extract `annotate_type` attributes from the `TypeLoc`.
Note: One might ask why all this mechanism is necessary. Couldn't the attribute
arguments be placed on the `AttributedType`, so that we could extract them
without having to access the `TypeLoc`?
However, putting the attribute arguments on the `AttributedType` would violate
the conceptual distinction between `Type` and `TypeLoc`. The attribute arguments
are `Expr`s, which are associated with a specific location in the source code.
In contrast, a `Type` is intended to be independent of source location.
PiperOrigin-RevId: 456703847
diff --git a/lifetime_analysis/builtin_lifetimes.cc b/lifetime_analysis/builtin_lifetimes.cc
index e20577e..ee43929 100644
--- a/lifetime_analysis/builtin_lifetimes.cc
+++ b/lifetime_analysis/builtin_lifetimes.cc
@@ -26,13 +26,13 @@
class ForwardAndMoveFactory : public FunctionLifetimeFactory {
llvm::Expected<ValueLifetimes> CreateParamLifetimes(
- clang::QualType type) const override {
+ clang::QualType type, clang::TypeLoc) const override {
return ValueLifetimes::Create(
type, [](const clang::Expr*) { return Lifetime::CreateVariable(); });
}
llvm::Expected<ValueLifetimes> CreateReturnLifetimes(
- clang::QualType type,
+ clang::QualType type, clang::TypeLoc,
const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
const std::optional<ValueLifetimes>& /*this_lifetimes*/) const override {
assert(param_lifetimes.size() == 1);
diff --git a/lifetime_annotations/function_lifetimes.cc b/lifetime_annotations/function_lifetimes.cc
index 1ae177b..a1dbad7 100644
--- a/lifetime_annotations/function_lifetimes.cc
+++ b/lifetime_annotations/function_lifetimes.cc
@@ -28,14 +28,24 @@
method && !method->isStatic()) {
this_type = method->getThisType();
}
- return Create(func->getType()->getAs<clang::FunctionProtoType>(), this_type,
- lifetime_factory);
+ clang::TypeLoc type_loc;
+ if (func->getTypeSourceInfo()) {
+ type_loc = func->getTypeSourceInfo()->getTypeLoc();
+ }
+ return Create(func->getType()->getAs<clang::FunctionProtoType>(), type_loc,
+ this_type, lifetime_factory);
+}
+
+llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForFunctionType(
+ const clang::FunctionProtoType* func, clang::TypeLoc func_type_loc,
+ const FunctionLifetimeFactory& lifetime_factory) {
+ return Create(func, func_type_loc, clang::QualType(), lifetime_factory);
}
llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForFunctionType(
const clang::FunctionProtoType* func,
const FunctionLifetimeFactory& lifetime_factory) {
- return Create(func, clang::QualType(), lifetime_factory);
+ return CreateForFunctionType(func, clang::TypeLoc(), lifetime_factory);
}
llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateCopy(
@@ -95,33 +105,53 @@
}
llvm::Expected<FunctionLifetimes> FunctionLifetimes::Create(
- const clang::FunctionProtoType* type, const clang::QualType this_type,
+ const clang::FunctionProtoType* type, clang::TypeLoc type_loc,
+ const clang::QualType this_type,
const FunctionLifetimeFactory& lifetime_factory) {
FunctionLifetimes ret;
if (!this_type.isNull()) {
ValueLifetimes tmp;
+ // TODO(mboehme): We don't have a `TypeLoc` for this because the `this` is
+ // never spelled out.
if (llvm::Error err =
- lifetime_factory.CreateParamLifetimes(this_type).moveInto(tmp)) {
+ lifetime_factory.CreateParamLifetimes(this_type, clang::TypeLoc())
+ .moveInto(tmp)) {
return std::move(err);
}
ret.this_lifetimes_ = std::move(tmp);
}
+ clang::FunctionTypeLoc func_type_loc;
+ if (type_loc) {
+ func_type_loc = type_loc.getAsAdjusted<clang::FunctionTypeLoc>();
+ }
ret.param_lifetimes_.reserve(type->getNumParams());
for (size_t i = 0; i < type->getNumParams(); i++) {
+ clang::TypeLoc param_type_loc;
+ if (type_loc) {
+ const clang::ParmVarDecl* param = func_type_loc.getParam(i);
+ if (param && param->getTypeSourceInfo()) {
+ param_type_loc = param->getTypeSourceInfo()->getTypeLoc();
+ }
+ }
ValueLifetimes tmp;
if (llvm::Error err =
- lifetime_factory.CreateParamLifetimes(type->getParamType(i))
+ lifetime_factory
+ .CreateParamLifetimes(type->getParamType(i), param_type_loc)
.moveInto(tmp)) {
return std::move(err);
}
ret.param_lifetimes_.push_back(std::move(tmp));
}
+ clang::TypeLoc return_type_loc;
+ if (func_type_loc) {
+ return_type_loc = func_type_loc.getReturnLoc();
+ }
if (llvm::Error err =
lifetime_factory
- .CreateReturnLifetimes(type->getReturnType(),
+ .CreateReturnLifetimes(type->getReturnType(), return_type_loc,
ret.param_lifetimes_, ret.this_lifetimes_)
.moveInto(ret.return_lifetimes_)) {
return std::move(err);
diff --git a/lifetime_annotations/function_lifetimes.h b/lifetime_annotations/function_lifetimes.h
index 6876b9e..960a24c 100644
--- a/lifetime_annotations/function_lifetimes.h
+++ b/lifetime_annotations/function_lifetimes.h
@@ -29,10 +29,14 @@
class FunctionLifetimeFactory {
public:
virtual ~FunctionLifetimeFactory() {}
+
+ // Note: The `type_loc` parameter passed into `CreateParamLifetimes` and
+ // `CreateReturnLifetimes` may be null if no type location is available.
+
virtual llvm::Expected<ValueLifetimes> CreateParamLifetimes(
- clang::QualType type) const = 0;
+ clang::QualType type, clang::TypeLoc type_loc) const = 0;
virtual llvm::Expected<ValueLifetimes> CreateReturnLifetimes(
- clang::QualType type,
+ clang::QualType type, clang::TypeLoc type_loc,
const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
const std::optional<ValueLifetimes>& this_lifetimes) const = 0;
};
@@ -43,14 +47,14 @@
FunctionLifetimeFactorySingleCallback(LifetimeFactory factory)
: factory_(std::move(factory)) {}
llvm::Expected<ValueLifetimes> CreateParamLifetimes(
- clang::QualType type) const override {
- return ValueLifetimes::Create(type, factory_);
+ clang::QualType type, clang::TypeLoc type_loc) const override {
+ return ValueLifetimes::Create(type, type_loc, factory_);
}
llvm::Expected<ValueLifetimes> CreateReturnLifetimes(
- clang::QualType type,
+ clang::QualType type, clang::TypeLoc type_loc,
const llvm::SmallVector<ValueLifetimes>& /*param_lifetimes*/,
const std::optional<ValueLifetimes>& /*this_lifetimes*/) const override {
- return ValueLifetimes::Create(type, factory_);
+ return ValueLifetimes::Create(type, type_loc, factory_);
}
private:
@@ -90,6 +94,9 @@
const FunctionLifetimeFactory& lifetime_factory);
static llvm::Expected<FunctionLifetimes> CreateForFunctionType(
+ const clang::FunctionProtoType* function, clang::TypeLoc func_type_loc,
+ const FunctionLifetimeFactory& lifetime_factory);
+ static llvm::Expected<FunctionLifetimes> CreateForFunctionType(
const clang::FunctionProtoType* function,
const FunctionLifetimeFactory& lifetime_factory);
@@ -148,7 +155,8 @@
std::optional<ValueLifetimes> this_lifetimes_;
static llvm::Expected<FunctionLifetimes> Create(
- const clang::FunctionProtoType* type, const clang::QualType this_type,
+ const clang::FunctionProtoType* type, clang::TypeLoc type_loc,
+ const clang::QualType this_type,
const FunctionLifetimeFactory& lifetime_factory);
};
diff --git a/lifetime_annotations/lifetime_annotations.cc b/lifetime_annotations/lifetime_annotations.cc
index ae4c7f0..d3e4021 100644
--- a/lifetime_annotations/lifetime_annotations.cc
+++ b/lifetime_annotations/lifetime_annotations.cc
@@ -153,7 +153,7 @@
private:
llvm::Expected<ValueLifetimes> CreateParamLifetimes(
- clang::QualType param_type) const override {
+ clang::QualType param_type, clang::TypeLoc) const override {
// TODO(mboehme): parse lifetime annotations from `type` if present.
return ValueLifetimes::Create(
param_type, [this](const clang::Expr*) -> llvm::Expected<Lifetime> {
@@ -204,7 +204,7 @@
}
llvm::Expected<ValueLifetimes> CreateReturnLifetimes(
- clang::QualType return_type,
+ clang::QualType return_type, clang::TypeLoc,
const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
const std::optional<ValueLifetimes>& this_lifetimes) const override {
// TODO(mboehme): parse lifetime annotations from `type` if present.
diff --git a/lifetime_annotations/pointee_type.cc b/lifetime_annotations/pointee_type.cc
index f33d48b..40ede20 100644
--- a/lifetime_annotations/pointee_type.cc
+++ b/lifetime_annotations/pointee_type.cc
@@ -18,6 +18,19 @@
return clang::QualType();
}
+clang::TypeLoc PointeeTypeLoc(clang::TypeLoc type_loc) {
+ type_loc = type_loc.getUnqualifiedLoc();
+
+ if (auto pointer_type_loc = type_loc.getAs<clang::PointerTypeLoc>()) {
+ return pointer_type_loc.getPointeeLoc();
+ } else if (auto reference_type_loc =
+ type_loc.getAs<clang::ReferenceTypeLoc>()) {
+ return reference_type_loc.getPointeeLoc();
+ }
+
+ return clang::TypeLoc();
+}
+
} // namespace lifetimes
} // namespace tidy
} // namespace clang
diff --git a/lifetime_annotations/pointee_type.h b/lifetime_annotations/pointee_type.h
index b68d1ed..b340e6e 100644
--- a/lifetime_annotations/pointee_type.h
+++ b/lifetime_annotations/pointee_type.h
@@ -6,6 +6,7 @@
#define CRUBIT_LIFETIME_ANNOTATIONS_POINTEE_TYPE_H_
#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
namespace clang {
namespace tidy {
@@ -20,6 +21,9 @@
// lifetimes for it".
clang::QualType PointeeType(clang::QualType type);
+// Analogous to `PointeeType` but operates on a `TypeLoc`.
+clang::TypeLoc PointeeTypeLoc(clang::TypeLoc type_loc);
+
} // namespace lifetimes
} // namespace tidy
} // namespace clang
diff --git a/lifetime_annotations/type_lifetimes.cc b/lifetime_annotations/type_lifetimes.cc
index 790ace4..b8237e2 100644
--- a/lifetime_annotations/type_lifetimes.cc
+++ b/lifetime_annotations/type_lifetimes.cc
@@ -126,27 +126,54 @@
namespace {
llvm::Error ForEachTemplateArgument(
- clang::QualType type,
- const std::function<llvm::Error(int, clang::QualType)>& callback) {
+ clang::QualType type, clang::TypeLoc type_loc,
+ const std::function<llvm::Error(int, clang::QualType, clang::TypeLoc)>&
+ callback) {
llvm::SmallVector<llvm::ArrayRef<clang::TemplateArgument>> template_args =
GetTemplateArgs(type);
- for (size_t depth = 0; depth < template_args.size(); depth++) {
+ std::optional<llvm::SmallVector<llvm::SmallVector<clang::TypeLoc>>>
+ maybe_template_arg_locs;
+ if (type_loc) {
+ maybe_template_arg_locs = GetTemplateArgs(type_loc);
+ }
+ llvm::SmallVector<llvm::SmallVector<clang::TypeLoc>> template_arg_locs;
+ if (maybe_template_arg_locs) {
+ template_arg_locs = *std::move(maybe_template_arg_locs);
+ } else {
+ // Fill all of `template_arg_locs` with empty `TypeLoc`s so we don't need to
+ // make any case distinctions below.
+ template_arg_locs.resize(template_args.size());
+ for (size_t depth = 0; depth < template_args.size(); depth++) {
+ template_arg_locs[depth].resize(template_args[depth].size());
+ }
+ }
+ assert(template_arg_locs.size() == template_args.size());
+ for (int depth = 0; depth < template_args.size(); depth++) {
const auto& args_at_depth = template_args[depth];
- for (const clang::TemplateArgument& arg : args_at_depth) {
+ const auto& arg_locs_at_depth = template_arg_locs[depth];
+ assert(args_at_depth.size() == arg_locs_at_depth.size());
+ for (size_t i = 0; i < args_at_depth.size(); ++i) {
+ const clang::TemplateArgument& arg = args_at_depth[i];
if (arg.getKind() == clang::TemplateArgument::Type) {
- if (llvm::Error err = callback(depth, arg.getAsType())) {
+ if (llvm::Error err =
+ callback(depth, arg.getAsType(), arg_locs_at_depth[i])) {
return err;
}
} else if (arg.getKind() == clang::TemplateArgument::Pack) {
for (const clang::TemplateArgument& inner_arg : arg.getPackAsArray()) {
if (inner_arg.getKind() == clang::TemplateArgument::Type) {
- if (llvm::Error err = callback(depth, inner_arg.getAsType())) {
+ // TODO(mboehme): Pass on the correct TypeLoc() in this case (if
+ // that's even possible -- I have to admit I'm not sure about how
+ // this case would get triggered)
+ if (llvm::Error err =
+ callback(depth, inner_arg.getAsType(), clang::TypeLoc())) {
return err;
}
}
}
} else {
- if (llvm::Error err = callback(depth, clang::QualType())) {
+ if (llvm::Error err =
+ callback(depth, clang::QualType(), clang::TypeLoc())) {
return err;
}
}
@@ -155,11 +182,35 @@
return llvm::Error::success();
}
+QualType Undecay(clang::QualType type) {
+ if (auto decayed = type->getAs<clang::DecayedType>()) {
+ return decayed->getOriginalType();
+ }
+ return type;
+}
+
+bool SameType(clang::QualType type1, clang::QualType type2) {
+ // If both types are an AutoType, ignore the actual type and assume they're
+ // the same.
+ // An `AutoType` that came from a TypeLoc will have type `auto` (i.e. as
+ // written), whereas an `AutoType` that didn't come from a `TypeLoc` will be
+ // the actual deduced type. We still want these to compare equal though.
+ if (type1->getAs<clang::AutoType>() && type2->getAs<clang::AutoType>()) {
+ return true;
+ }
+ return Undecay(type1) == Undecay(type2);
+}
+
} // namespace
llvm::Expected<ValueLifetimes> ValueLifetimes::Create(
- clang::QualType type, LifetimeFactory lifetime_factory) {
+ clang::QualType type, clang::TypeLoc type_loc,
+ LifetimeFactory lifetime_factory) {
assert(!type.isNull());
+ if (type_loc) {
+ assert(SameType(type_loc.getType(), type));
+ }
+
type = type.IgnoreParens();
ValueLifetimes ret(type);
@@ -168,8 +219,9 @@
// parameter and return lifetimes.
FunctionLifetimeFactorySingleCallback factory(lifetime_factory);
FunctionLifetimes fn_lftm;
- if (llvm::Error err = FunctionLifetimes::CreateForFunctionType(fn, factory)
- .moveInto(fn_lftm)) {
+ if (llvm::Error err =
+ FunctionLifetimes::CreateForFunctionType(fn, type_loc, factory)
+ .moveInto(fn_lftm)) {
return std::move(err);
}
ret.function_lifetimes_ =
@@ -188,14 +240,16 @@
// Add implicit lifetime parameters for type template parameters.
if (llvm::Error err = ForEachTemplateArgument(
- type,
- [&ret, &lifetime_factory](int depth,
- clang::QualType arg_type) -> llvm::Error {
+ type, type_loc,
+ [&ret, &lifetime_factory](
+ int depth, clang::QualType arg_type,
+ clang::TypeLoc arg_type_loc) -> llvm::Error {
std::optional<ValueLifetimes> maybe_template_arg_lifetime;
if (!arg_type.isNull()) {
maybe_template_arg_lifetime.emplace();
if (llvm::Error err =
- ValueLifetimes::Create(arg_type, lifetime_factory)
+ ValueLifetimes::Create(arg_type, arg_type_loc,
+ lifetime_factory)
.moveInto(*maybe_template_arg_lifetime)) {
return err;
}
@@ -212,31 +266,35 @@
clang::QualType pointee = PointeeType(type);
if (pointee.isNull()) return ret;
- ObjectLifetimes obj_lftm;
- if (llvm::Error err = ObjectLifetimes::Create(pointee, lifetime_factory)
- .moveInto(obj_lftm)) {
+
+ clang::TypeLoc pointee_type_loc;
+ if (type_loc) {
+ pointee_type_loc = PointeeTypeLoc(type_loc);
+ // Note: We can't assert that `pointee_type_loc` is non-null here. If
+ // `type_loc` is a `TypedefTypeLoc`, then there will be no `TypeLoc` for
+ // the pointee type because the pointee type never got spelled out at the
+ // location of the original `TypeLoc`.
+ }
+
+ ValueLifetimes value_lifetimes;
+ if (llvm::Error err =
+ ValueLifetimes::Create(pointee, pointee_type_loc, lifetime_factory)
+ .moveInto(value_lifetimes)) {
+ return std::move(err);
+ }
+
+ Lifetime object_lifetime;
+ // TODO(mboehme): Pass the correct lifetime name.
+ const clang::Expr* lifetime_name = nullptr;
+ if (llvm::Error err =
+ lifetime_factory(lifetime_name).moveInto(object_lifetime)) {
return std::move(err);
}
ret.pointee_lifetimes_ =
- std::make_unique<ObjectLifetimes>(std::move(obj_lftm));
+ std::make_unique<ObjectLifetimes>(object_lifetime, value_lifetimes);
return ret;
}
-llvm::Expected<ObjectLifetimes> ObjectLifetimes::Create(
- clang::QualType type, LifetimeFactory lifetime_factory) {
- ValueLifetimes v;
- if (llvm::Error err =
- ValueLifetimes::Create(type, lifetime_factory).moveInto(v)) {
- return std::move(err);
- }
- Lifetime l;
- // TODO(mboehme): Pass lifetime name.
- if (llvm::Error err = lifetime_factory(nullptr).moveInto(l)) {
- return std::move(err);
- }
- return ObjectLifetimes(l, v);
-}
-
ValueLifetimes ValueLifetimes::ForLifetimeLessType(clang::QualType type) {
assert(PointeeType(type).isNull() && !type->isRecordType());
return ValueLifetimes(type);
diff --git a/lifetime_annotations/type_lifetimes.h b/lifetime_annotations/type_lifetimes.h
index f440970..e0d83ec 100644
--- a/lifetime_annotations/type_lifetimes.h
+++ b/lifetime_annotations/type_lifetimes.h
@@ -68,8 +68,21 @@
// Creates a ValueLifetimes for a *value* of a given type.
// Only fails if lifetime_factory fails.
// Lifetimes will be created in post-order in the tree of lifetimes.
+ // If a non-null `type_loc` is passed in, passes any lifetime names
+ // found in `annotate_type` attributes on to `lifetime_factory`.
+ // Note: It's necessary to pass in both `type` and `type_loc` because we may
+ // not be able to traverse as deeply into `type_loc` as we can into `type`.
+ // For example, if the type is a typedef for a pointer type, we can traverse
+ // into that pointer type through `type`, but not through `type_loc`. This is
+ // because the pointer type is not spelled out in the place where the
+ // `type_loc` appears in the source code.
static llvm::Expected<ValueLifetimes> Create(
- clang::QualType type, LifetimeFactory lifetime_factory);
+ clang::QualType type, clang::TypeLoc type_loc,
+ LifetimeFactory lifetime_factory);
+ static llvm::Expected<ValueLifetimes> Create(
+ clang::QualType type, LifetimeFactory lifetime_factory) {
+ return Create(type, clang::TypeLoc(), lifetime_factory);
+ }
// Returns a ValueLifetimes for a lifetime-less type.
// `type` must not be a pointer-like type or a record type.