[clang] bebb7fe - [clang][NFC] Pass/Return `CharUnits` by value (#226115)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 27 20:35:08 PDT 2026
Author: Timm Baeder
Date: 2026-09-28T05:35:01+02:00
New Revision: bebb7fe62f8883d822f084687cfb84c5cfe12bbb
URL: https://github.com/llvm/llvm-project/commit/bebb7fe62f8883d822f084687cfb84c5cfe12bbb
DIFF: https://github.com/llvm/llvm-project/commit/bebb7fe62f8883d822f084687cfb84c5cfe12bbb.diff
LOG: [clang][NFC] Pass/Return `CharUnits` by value (#226115)
`CharUnits` is just an `int64_t`, but passed around by const reference
for some reason.
Also update the `CharUnits` documentation since we touch most of the
lines anyway (due to clang-format).
Added:
Modified:
clang/include/clang/AST/APValue.h
clang/include/clang/AST/CharUnits.h
clang/lib/AST/APValue.cpp
clang/lib/AST/ASTContext.cpp
clang/lib/AST/ExprConstant.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h
index ef25f99b48544..45a08e4d614ef 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_APVALUE_H
#define LLVM_CLANG_AST_APVALUE_H
+#include "clang/AST/CharUnits.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/APFixedPoint.h"
#include "llvm/ADT/APFloat.h"
@@ -30,7 +31,6 @@ template <typename T> class BasicReaderBase;
class AddrLabelExpr;
class ASTContext;
- class CharUnits;
class CXXRecordDecl;
class Decl;
class DiagnosticBuilder;
@@ -376,7 +376,7 @@ class LLVM_ATTRIBUTE_WARN_UNUSED APValue {
/// \param Base The base of the lvalue.
/// \param Offset The offset of the lvalue.
/// \param IsNullPtr Whether this lvalue is a null pointer.
- APValue(LValueBase Base, const CharUnits &Offset, NoLValuePath,
+ APValue(LValueBase Base, CharUnits Offset, NoLValuePath,
bool IsNullPtr = false)
: Kind(None), AllowConstexprUnknown(false) {
MakeLValue();
@@ -389,9 +389,8 @@ class LLVM_ATTRIBUTE_WARN_UNUSED APValue {
/// \param OnePastTheEnd Whether this lvalue is one-past-the-end of the
/// subobject it points to.
/// \param IsNullPtr Whether this lvalue is a null pointer.
- APValue(LValueBase Base, const CharUnits &Offset,
- ArrayRef<LValuePathEntry> Path, bool OnePastTheEnd,
- bool IsNullPtr = false)
+ APValue(LValueBase Base, CharUnits Offset, ArrayRef<LValuePathEntry> Path,
+ bool OnePastTheEnd, bool IsNullPtr = false)
: Kind(None), AllowConstexprUnknown(false) {
MakeLValue();
setLValue(Base, Offset, Path, OnePastTheEnd, IsNullPtr);
@@ -400,7 +399,7 @@ class LLVM_ATTRIBUTE_WARN_UNUSED APValue {
/// \param Base The base of the lvalue.
/// \param Offset The offset of the lvalue.
/// \param IsNullPtr Whether this lvalue is a null pointer.
- APValue(LValueBase Base, const CharUnits &Offset, ConstexprUnknown,
+ APValue(LValueBase Base, CharUnits Offset, ConstexprUnknown,
bool IsNullPtr = false)
: Kind(None), AllowConstexprUnknown(true) {
MakeLValue();
@@ -573,7 +572,7 @@ class LLVM_ATTRIBUTE_WARN_UNUSED APValue {
const LValueBase getLValueBase() const;
CharUnits &getLValueOffset();
- const CharUnits &getLValueOffset() const {
+ CharUnits getLValueOffset() const {
return const_cast<APValue*>(this)->getLValueOffset();
}
bool isLValueOnePastTheEnd() const;
@@ -754,11 +753,9 @@ class LLVM_ATTRIBUTE_WARN_UNUSED APValue {
((ComplexAPFloat *)(char *)&Data)->Real = std::move(R);
((ComplexAPFloat *)(char *)&Data)->Imag = std::move(I);
}
- void setLValue(LValueBase B, const CharUnits &O, NoLValuePath,
- bool IsNullPtr);
- void setLValue(LValueBase B, const CharUnits &O,
- ArrayRef<LValuePathEntry> Path, bool OnePastTheEnd,
- bool IsNullPtr);
+ void setLValue(LValueBase B, CharUnits O, NoLValuePath, bool IsNullPtr);
+ void setLValue(LValueBase B, CharUnits O, ArrayRef<LValuePathEntry> Path,
+ bool OnePastTheEnd, bool IsNullPtr);
void setUnion(const FieldDecl *Field, const APValue &Value);
void setAddrLabelDiff(const AddrLabelExpr* LHSExpr,
const AddrLabelExpr* RHSExpr) {
@@ -853,9 +850,10 @@ class LLVM_ATTRIBUTE_WARN_UNUSED APValue {
M->NumCols = NumCols;
return {M->Elts, NumElts};
}
- MutableArrayRef<LValuePathEntry>
- setLValueUninit(LValueBase B, const CharUnits &O, unsigned Size,
- bool OnePastTheEnd, bool IsNullPtr);
+ MutableArrayRef<LValuePathEntry> setLValueUninit(LValueBase B, CharUnits O,
+ unsigned Size,
+ bool OnePastTheEnd,
+ bool IsNullPtr);
MutableArrayRef<const CXXRecordDecl *>
setMemberPointerUninit(const ValueDecl *Member, bool IsDerivedMember,
unsigned Size);
diff --git a/clang/include/clang/AST/CharUnits.h b/clang/include/clang/AST/CharUnits.h
index aee838e60f948..e432570128e6f 100644
--- a/clang/include/clang/AST/CharUnits.h
+++ b/clang/include/clang/AST/CharUnits.h
@@ -20,223 +20,189 @@
namespace clang {
- /// CharUnits - This is an opaque type for sizes expressed in character units.
- /// Instances of this type represent a quantity as a multiple of the size
- /// of the standard C type, char, on the target architecture. As an opaque
- /// type, CharUnits protects you from accidentally combining operations on
- /// quantities in bit units and character units.
- ///
- /// In both C and C++, an object of type 'char', 'signed char', or 'unsigned
- /// char' occupies exactly one byte, so 'character unit' and 'byte' refer to
- /// the same quantity of storage. However, we use the term 'character unit'
- /// rather than 'byte' to avoid an implication that a character unit is
- /// exactly 8 bits.
+/// This is an opaque type for sizes expressed in character units.
+/// Instances of this type represent a quantity as a multiple of the size
+/// of the standard C type, char, on the target architecture. As an opaque
+/// type, CharUnits protects you from accidentally combining operations on
+/// quantities in bit units and character units.
+///
+/// In both C and C++, an object of type 'char', 'signed char', or 'unsigned
+/// char' occupies exactly one byte, so 'character unit' and 'byte' refer to
+/// the same quantity of storage. However, we use the term 'character unit'
+/// rather than 'byte' to avoid an implication that a character unit is
+/// exactly 8 bits.
+///
+/// For portability, never assume that a target character is 8 bits wide. Use
+/// CharUnit values wherever you calculate sizes, offsets, or alignments
+/// in character units.
+class CharUnits {
+public:
+ typedef int64_t QuantityType;
+
+private:
+ QuantityType Quantity = 0;
+
+ explicit CharUnits(QuantityType C) : Quantity(C) {}
+
+public:
+ /// A default constructor.
+ CharUnits() = default;
+
+ /// Construct a CharUnits quantity of zero.
+ static CharUnits Zero() { return CharUnits(0); }
+
+ /// Construct a CharUnits quantity of one.
+ static CharUnits One() { return CharUnits(1); }
+
+ /// Construct a CharUnits quantity from a raw integer type.
+ static CharUnits fromQuantity(QuantityType Quantity) {
+ return CharUnits(Quantity);
+ }
+
+ /// Construct a CharUnits quantity from an llvm::Align
+ /// quantity.
+ static CharUnits fromQuantity(llvm::Align Quantity) {
+ return CharUnits(Quantity.value());
+ }
+
+ // Compound assignment.
+ CharUnits &operator+=(CharUnits Other) {
+ Quantity += Other.Quantity;
+ return *this;
+ }
+ CharUnits &operator++() {
+ ++Quantity;
+ return *this;
+ }
+ CharUnits operator++(int) { return CharUnits(Quantity++); }
+ CharUnits &operator-=(CharUnits Other) {
+ Quantity -= Other.Quantity;
+ return *this;
+ }
+ CharUnits &operator--() {
+ --Quantity;
+ return *this;
+ }
+ CharUnits operator--(int) { return CharUnits(Quantity--); }
+
+ // Comparison operators.
+ bool operator==(CharUnits Other) const { return Quantity == Other.Quantity; }
+ bool operator!=(CharUnits Other) const { return Quantity != Other.Quantity; }
+
+ // Relational operators.
+ bool operator<(CharUnits Other) const { return Quantity < Other.Quantity; }
+ bool operator<=(CharUnits Other) const { return Quantity <= Other.Quantity; }
+ bool operator>(CharUnits Other) const { return Quantity > Other.Quantity; }
+ bool operator>=(CharUnits Other) const { return Quantity >= Other.Quantity; }
+
+ // Other predicates.
+
+ /// Test whether the quantity equals zero.
+ bool isZero() const { return Quantity == 0; }
+
+ /// Test whether the quantity equals one.
+ bool isOne() const { return Quantity == 1; }
+
+ /// Test whether the quantity is greater than zero.
+ bool isPositive() const { return Quantity > 0; }
+
+ /// Test whether the quantity is less than zero.
+ bool isNegative() const { return Quantity < 0; }
+
+ /// Test whether the quantity is a power of two.
+ /// Zero is not a power of two.
+ bool isPowerOfTwo() const { return (Quantity & -Quantity) == Quantity; }
+
+ /// Test whether this is a multiple of the other value.
///
- /// For portability, never assume that a target character is 8 bits wide. Use
- /// CharUnit values wherever you calculate sizes, offsets, or alignments
- /// in character units.
- class CharUnits {
- public:
- typedef int64_t QuantityType;
-
- private:
- QuantityType Quantity = 0;
-
- explicit CharUnits(QuantityType C) : Quantity(C) {}
-
- public:
-
- /// CharUnits - A default constructor.
- CharUnits() = default;
-
- /// Zero - Construct a CharUnits quantity of zero.
- static CharUnits Zero() {
- return CharUnits(0);
- }
-
- /// One - Construct a CharUnits quantity of one.
- static CharUnits One() {
- return CharUnits(1);
- }
-
- /// fromQuantity - Construct a CharUnits quantity from a raw integer type.
- static CharUnits fromQuantity(QuantityType Quantity) {
- return CharUnits(Quantity);
- }
-
- /// fromQuantity - Construct a CharUnits quantity from an llvm::Align
- /// quantity.
- static CharUnits fromQuantity(llvm::Align Quantity) {
- return CharUnits(Quantity.value());
- }
-
- // Compound assignment.
- CharUnits& operator+= (const CharUnits &Other) {
- Quantity += Other.Quantity;
- return *this;
- }
- CharUnits& operator++ () {
- ++Quantity;
- return *this;
- }
- CharUnits operator++ (int) {
- return CharUnits(Quantity++);
- }
- CharUnits& operator-= (const CharUnits &Other) {
- Quantity -= Other.Quantity;
- return *this;
- }
- CharUnits& operator-- () {
- --Quantity;
- return *this;
- }
- CharUnits operator-- (int) {
- return CharUnits(Quantity--);
- }
-
- // Comparison operators.
- bool operator== (const CharUnits &Other) const {
- return Quantity == Other.Quantity;
- }
- bool operator!= (const CharUnits &Other) const {
- return Quantity != Other.Quantity;
- }
-
- // Relational operators.
- bool operator< (const CharUnits &Other) const {
- return Quantity < Other.Quantity;
- }
- bool operator<= (const CharUnits &Other) const {
- return Quantity <= Other.Quantity;
- }
- bool operator> (const CharUnits &Other) const {
- return Quantity > Other.Quantity;
- }
- bool operator>= (const CharUnits &Other) const {
- return Quantity >= Other.Quantity;
- }
-
- // Other predicates.
-
- /// isZero - Test whether the quantity equals zero.
- bool isZero() const { return Quantity == 0; }
-
- /// isOne - Test whether the quantity equals one.
- bool isOne() const { return Quantity == 1; }
-
- /// isPositive - Test whether the quantity is greater than zero.
- bool isPositive() const { return Quantity > 0; }
-
- /// isNegative - Test whether the quantity is less than zero.
- bool isNegative() const { return Quantity < 0; }
-
- /// isPowerOfTwo - Test whether the quantity is a power of two.
- /// Zero is not a power of two.
- bool isPowerOfTwo() const {
- return (Quantity & -Quantity) == Quantity;
- }
-
- /// Test whether this is a multiple of the other value.
- ///
- /// Among other things, this promises that
- /// self.alignTo(N) will just return self.
- bool isMultipleOf(CharUnits N) const {
- return (*this % N) == CharUnits::Zero();
- }
-
- // Arithmetic operators.
- CharUnits operator* (QuantityType N) const {
- return CharUnits(Quantity * N);
- }
- CharUnits &operator*= (QuantityType N) {
- Quantity *= N;
- return *this;
- }
- CharUnits operator/ (QuantityType N) const {
- return CharUnits(Quantity / N);
- }
- CharUnits &operator/= (QuantityType N) {
- Quantity /= N;
- return *this;
- }
- QuantityType operator/ (const CharUnits &Other) const {
- return Quantity / Other.Quantity;
- }
- CharUnits operator% (QuantityType N) const {
- return CharUnits(Quantity % N);
- }
- CharUnits operator%(const CharUnits &Other) const {
- return CharUnits(Quantity % Other.Quantity);
- }
- CharUnits operator+ (const CharUnits &Other) const {
- return CharUnits(Quantity + Other.Quantity);
- }
- CharUnits operator- (const CharUnits &Other) const {
- return CharUnits(Quantity - Other.Quantity);
- }
- CharUnits operator- () const {
- return CharUnits(-Quantity);
- }
-
-
- // Conversions.
-
- /// getQuantity - Get the raw integer representation of this quantity.
- QuantityType getQuantity() const { return Quantity; }
-
- /// getAsAlign - Returns Quantity as a valid llvm::Align,
- /// Beware llvm::Align assumes power of two 8-bit bytes.
- llvm::Align getAsAlign() const { return llvm::Align(Quantity); }
-
- /// getAsMaybeAlign - Returns Quantity as a valid llvm::Align or
- /// std::nullopt, Beware llvm::MaybeAlign assumes power of two 8-bit
- /// bytes.
- llvm::MaybeAlign getAsMaybeAlign() const {
- return llvm::MaybeAlign(Quantity);
- }
-
- /// alignTo - Returns the next integer (mod 2**64) that is
- /// greater than or equal to this quantity and is a multiple of \p Align.
- /// Align must be non-zero.
- CharUnits alignTo(const CharUnits &Align) const {
- return CharUnits(llvm::alignTo(Quantity, Align.Quantity));
- }
-
- /// Given that this is a non-zero alignment value, what is the
- /// alignment at the given offset?
- CharUnits alignmentAtOffset(CharUnits offset) const {
- assert(Quantity != 0 && "offsetting from unknown alignment?");
- return CharUnits(llvm::MinAlign(Quantity, offset.Quantity));
- }
-
- /// Given that this is the alignment of the first element of an
- /// array, return the minimum alignment of any element in the array.
- CharUnits alignmentOfArrayElement(CharUnits elementSize) const {
- // Since we don't track offsetted alignments, the alignment of
- // the second element (or any odd element) will be minimally
- // aligned.
- return alignmentAtOffset(elementSize);
- }
-
-
- }; // class CharUnit
+ /// Among other things, this promises that
+ /// self.alignTo(N) will just return self.
+ bool isMultipleOf(CharUnits N) const {
+ return (*this % N) == CharUnits::Zero();
+ }
+
+ // Arithmetic operators.
+ CharUnits operator*(QuantityType N) const { return CharUnits(Quantity * N); }
+ CharUnits &operator*=(QuantityType N) {
+ Quantity *= N;
+ return *this;
+ }
+ CharUnits operator/(QuantityType N) const { return CharUnits(Quantity / N); }
+ CharUnits &operator/=(QuantityType N) {
+ Quantity /= N;
+ return *this;
+ }
+ QuantityType operator/(CharUnits Other) const {
+ return Quantity / Other.Quantity;
+ }
+ CharUnits operator%(QuantityType N) const { return CharUnits(Quantity % N); }
+ CharUnits operator%(CharUnits Other) const {
+ return CharUnits(Quantity % Other.Quantity);
+ }
+ CharUnits operator+(CharUnits Other) const {
+ return CharUnits(Quantity + Other.Quantity);
+ }
+ CharUnits operator-(CharUnits Other) const {
+ return CharUnits(Quantity - Other.Quantity);
+ }
+ CharUnits operator-() const { return CharUnits(-Quantity); }
+
+ // Conversions.
+
+ /// Get the raw integer representation of this quantity.
+ QuantityType getQuantity() const { return Quantity; }
+
+ /// Returns Quantity as a valid llvm::Align,
+ /// Beware llvm::Align assumes power of two 8-bit bytes.
+ llvm::Align getAsAlign() const { return llvm::Align(Quantity); }
+
+ /// Returns Quantity as a valid llvm::Align or
+ /// std::nullopt, Beware llvm::MaybeAlign assumes power of two 8-bit
+ /// bytes.
+ llvm::MaybeAlign getAsMaybeAlign() const {
+ return llvm::MaybeAlign(Quantity);
+ }
+
+ /// Returns the next integer (mod 2**64) that is
+ /// greater than or equal to this quantity and is a multiple of \p Align.
+ /// Align must be non-zero.
+ CharUnits alignTo(CharUnits Align) const {
+ return CharUnits(llvm::alignTo(Quantity, Align.Quantity));
+ }
+
+ /// Given that this is a non-zero alignment value, what is the
+ /// alignment at the given offset?
+ CharUnits alignmentAtOffset(CharUnits offset) const {
+ assert(Quantity != 0 && "offsetting from unknown alignment?");
+ return CharUnits(llvm::MinAlign(Quantity, offset.Quantity));
+ }
+
+ /// Given that this is the alignment of the first element of an
+ /// array, return the minimum alignment of any element in the array.
+ CharUnits alignmentOfArrayElement(CharUnits elementSize) const {
+ // Since we don't track offsetted alignments, the alignment of
+ // the second element (or any odd element) will be minimally
+ // aligned.
+ return alignmentAtOffset(elementSize);
+ }
+
+}; // class CharUnit
} // namespace clang
-inline clang::CharUnits operator* (clang::CharUnits::QuantityType Scale,
- const clang::CharUnits &CU) {
+inline clang::CharUnits operator*(clang::CharUnits::QuantityType Scale,
+ clang::CharUnits CU) {
return CU * Scale;
}
namespace llvm {
template<> struct DenseMapInfo<clang::CharUnits> {
- static unsigned getHashValue(const clang::CharUnits &CU) {
+ static unsigned getHashValue(clang::CharUnits CU) {
clang::CharUnits::QuantityType Quantity = CU.getQuantity();
return DenseMapInfo<clang::CharUnits::QuantityType>::getHashValue(Quantity);
}
- static bool isEqual(const clang::CharUnits &LHS,
- const clang::CharUnits &RHS) {
+ static bool isEqual(clang::CharUnits LHS, clang::CharUnits RHS) {
return LHS == RHS;
}
};
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index 3e61281fabd3f..4f42b7f5f037b 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -13,7 +13,6 @@
#include "clang/AST/APValue.h"
#include "Linkage.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
@@ -1050,7 +1049,7 @@ bool APValue::isNullPointer() const {
return ((const LV *)(const char *)&Data)->IsNullPtr;
}
-void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath,
+void APValue::setLValue(LValueBase B, CharUnits O, NoLValuePath,
bool IsNullPtr) {
assert(isLValue() && "Invalid accessor");
LV &LVal = *((LV *)(char *)&Data);
@@ -1062,7 +1061,7 @@ void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath,
}
MutableArrayRef<APValue::LValuePathEntry>
-APValue::setLValueUninit(LValueBase B, const CharUnits &O, unsigned Size,
+APValue::setLValueUninit(LValueBase B, CharUnits O, unsigned Size,
bool IsOnePastTheEnd, bool IsNullPtr) {
assert(isLValue() && "Invalid accessor");
LV &LVal = *((LV *)(char *)&Data);
@@ -1074,7 +1073,7 @@ APValue::setLValueUninit(LValueBase B, const CharUnits &O, unsigned Size,
return {LVal.getPath(), Size};
}
-void APValue::setLValue(LValueBase B, const CharUnits &O,
+void APValue::setLValue(LValueBase B, CharUnits O,
ArrayRef<LValuePathEntry> Path, bool IsOnePastTheEnd,
bool IsNullPtr) {
MutableArrayRef<APValue::LValuePathEntry> InternalPath =
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 5e3296a418c90..2c0db63d1fcfd 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -8878,7 +8878,7 @@ ASTContext::getInlineVariableDefinitionKind(const VarDecl *VD) const {
return InlineVariableDefinitionKind::WeakUnknown;
}
-static std::string charUnitsToString(const CharUnits &CU) {
+static std::string charUnitsToString(CharUnits CU) {
return llvm::itostr(CU.getQuantity());
}
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f7faffc767c1c..e2430959e1630 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1448,7 +1448,7 @@ namespace {
const APValue::LValueBase getLValueBase() const { return Base; }
bool allowConstexprUnknown() const { return AllowConstexprUnknown; }
CharUnits &getLValueOffset() { return Offset; }
- const CharUnits &getLValueOffset() const { return Offset; }
+ CharUnits getLValueOffset() const { return Offset; }
SubobjectDesignator &getLValueDesignator() { return Designator; }
const SubobjectDesignator &getLValueDesignator() const { return Designator;}
bool isNullPointer() const { return IsNullPtr;}
@@ -19392,8 +19392,8 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
return Success(CmpResult::Unequal, E);
}
- const CharUnits &LHSOffset = LHSValue.getLValueOffset();
- const CharUnits &RHSOffset = RHSValue.getLValueOffset();
+ CharUnits LHSOffset = LHSValue.getLValueOffset();
+ CharUnits RHSOffset = RHSValue.getLValueOffset();
SubobjectDesignator &LHSDesignator = LHSValue.getLValueDesignator();
SubobjectDesignator &RHSDesignator = RHSValue.getLValueDesignator();
@@ -19698,8 +19698,8 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
return Error(E);
return Success(APValue(LHSAddrExpr, RHSAddrExpr), E);
}
- const CharUnits &LHSOffset = LHSValue.getLValueOffset();
- const CharUnits &RHSOffset = RHSValue.getLValueOffset();
+ CharUnits LHSOffset = LHSValue.getLValueOffset();
+ CharUnits RHSOffset = RHSValue.getLValueOffset();
SubobjectDesignator &LHSDesignator = LHSValue.getLValueDesignator();
SubobjectDesignator &RHSDesignator = RHSValue.getLValueDesignator();
More information about the cfe-commits
mailing list