Skip to content

Commit

Permalink
[clang][C++20] P0960R3 and P1975R0: Allow initializing aggregates fro…
Browse files Browse the repository at this point in the history
…m a parenthesized list of values

This patch implements P0960R3, which allows initialization of aggregates
via parentheses.

As an example:

```
struct S { int i, j; };
S s1(1, 1);

int arr1[2](1, 2);
```

This patch also implements P1975R0, which fixes the wording of P0960R3
for single-argument parenthesized lists so that statements like the
following are allowed:

```
S s2(1);
S s3 = static_cast<S>(1);
S s4 = (S)1;

int (&&arr2)[] = static_cast<int[]>(1);
int (&&arr3)[2] = static_cast<int[2]>(1);
```

This patch was originally authored by @0x59616e and completed by
@ayzhao.

Fixes #54040, Fixes #54041

Co-authored-by: Sheng <ox59616e@gmail.com>

Full write up : https://discourse.llvm.org/t/c-20-rfc-suggestion-desired-regarding-the-implementation-of-p0960r3/63744

Reviewed By: ilya-biryukov

Differential Revision: https://reviews.llvm.org/D129531
  • Loading branch information
alanzhao1 committed Dec 14, 2022
1 parent c165b05 commit 40c5215
Show file tree
Hide file tree
Showing 42 changed files with 1,277 additions and 82 deletions.
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -697,6 +697,10 @@ C++20 Feature Support
(useful specially for constrained members). Fixes `GH50886 <https://github.com/llvm/llvm-project/issues/50886>`_.
- Implemented CWG2635 as a Defect Report, which prohibits structured bindings from being constrained.

- Implemented `P0960R3: <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0960r3.html>`_
and `P1975R0: <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1975r0.html>`_,
which allows parenthesized aggregate-initialization.

C++2b Feature Support
^^^^^^^^^^^^^^^^^^^^^

Expand Down
8 changes: 7 additions & 1 deletion clang/include/clang-c/Index.h
Expand Up @@ -1531,7 +1531,13 @@ enum CXCursorKind {
*/
CXCursor_RequiresExpr = 154,

CXCursor_LastExpr = CXCursor_RequiresExpr,
/**
* Expression that references a C++20 parenthesized list aggregate
* initializer.
*/
CXCursor_CXXParenListInitExpr = 155,

CXCursor_LastExpr = CXCursor_CXXParenListInitExpr,

/* Statements */
CXCursor_FirstStmt = 200,
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/AST/ASTNodeTraverser.h
Expand Up @@ -710,6 +710,12 @@ class ASTNodeTraverser
}
}

void VisitCXXParenListInitExpr(const CXXParenListInitExpr *PLIE) {
if (auto *Filler = PLIE->getArrayFiller()) {
Visit(Filler, "array_filler");
}
}

void VisitBlockExpr(const BlockExpr *Node) { Visit(Node->getBlockDecl()); }

void VisitOpaqueValueExpr(const OpaqueValueExpr *Node) {
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/ComputeDependence.h
Expand Up @@ -79,6 +79,7 @@ class CXXUnresolvedConstructExpr;
class CXXDependentScopeMemberExpr;
class MaterializeTemporaryExpr;
class CXXFoldExpr;
class CXXParenListInitExpr;
class TypeTraitExpr;
class ConceptSpecializationExpr;
class SYCLUniqueStableNameExpr;
Expand Down Expand Up @@ -168,6 +169,7 @@ ExprDependence computeDependence(CXXUnresolvedConstructExpr *E);
ExprDependence computeDependence(CXXDependentScopeMemberExpr *E);
ExprDependence computeDependence(MaterializeTemporaryExpr *E);
ExprDependence computeDependence(CXXFoldExpr *E);
ExprDependence computeDependence(CXXParenListInitExpr *E);
ExprDependence computeDependence(TypeTraitExpr *E);
ExprDependence computeDependence(ConceptSpecializationExpr *E,
bool ValueDependent);
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/AST/Decl.h
Expand Up @@ -914,7 +914,10 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
CallInit,

/// Direct list-initialization (C++11)
ListInit
ListInit,

/// Parenthesized list-initialization (C++20)
ParenListInit
};

/// Kinds of thread-local storage.
Expand Down
124 changes: 124 additions & 0 deletions clang/include/clang/AST/ExprCXX.h
Expand Up @@ -4713,6 +4713,130 @@ class CXXFoldExpr : public Expr {
}
};

/// Represents a list-initialization with parenthesis.
///
/// As per P0960R3, this is a C++20 feature that allows aggregate to
/// be initialized with a parenthesized list of values:
/// ```
/// struct A {
/// int a;
/// double b;
/// };
///
/// void foo() {
/// A a1(0); // Well-formed in C++20
/// A a2(1.5, 1.0); // Well-formed in C++20
/// }
/// ```
/// It has some sort of similiarity to braced
/// list-initialization, with some differences such as
/// it allows narrowing conversion whilst braced
/// list-initialization doesn't.
/// ```
/// struct A {
/// char a;
/// };
/// void foo() {
/// A a(1.5); // Well-formed in C++20
/// A b{1.5}; // Ill-formed !
/// }
/// ```
class CXXParenListInitExpr final
: public Expr,
private llvm::TrailingObjects<CXXParenListInitExpr, Expr *> {
friend class TrailingObjects;
friend class ASTStmtReader;
friend class ASTStmtWriter;

unsigned NumExprs;
unsigned NumUserSpecifiedExprs;
SourceLocation InitLoc, LParenLoc, RParenLoc;
Expr *ArrayFiller = nullptr;

CXXParenListInitExpr(ArrayRef<Expr *> Args, QualType T,
unsigned NumUserSpecifiedExprs, SourceLocation InitLoc,
SourceLocation LParenLoc, SourceLocation RParenLoc)
: Expr(CXXParenListInitExprClass, T,
T->isLValueReferenceType() ? VK_LValue
: T->isRValueReferenceType() ? VK_XValue
: VK_PRValue,
OK_Ordinary),
NumExprs(Args.size()), NumUserSpecifiedExprs(NumUserSpecifiedExprs),
InitLoc(InitLoc), LParenLoc(LParenLoc), RParenLoc(RParenLoc) {
std::copy(Args.begin(), Args.end(), getTrailingObjects<Expr *>());
assert(NumExprs >= NumUserSpecifiedExprs &&
"number of user specified inits is greater than the number of "
"passed inits");
setDependence(computeDependence(this));
}

size_t numTrailingObjects(OverloadToken<Expr *>) const { return NumExprs; }

public:
static CXXParenListInitExpr *
Create(ASTContext &C, ArrayRef<Expr *> Args, QualType T,
unsigned NumUserSpecifiedExprs, SourceLocation InitLoc,
SourceLocation LParenLoc, SourceLocation RParenLoc);

static CXXParenListInitExpr *CreateEmpty(ASTContext &C, unsigned numExprs,
EmptyShell Empty);

explicit CXXParenListInitExpr(EmptyShell Empty, unsigned NumExprs)
: Expr(CXXParenListInitExprClass, Empty), NumExprs(NumExprs),
NumUserSpecifiedExprs(0) {}

void updateDependence() { setDependence(computeDependence(this)); }

ArrayRef<Expr *> getInitExprs() {
return llvm::makeArrayRef(getTrailingObjects<Expr *>(), NumExprs);
}

const ArrayRef<Expr *> getInitExprs() const {
return llvm::makeArrayRef(getTrailingObjects<Expr *>(), NumExprs);
}

ArrayRef<Expr *> getUserSpecifiedInitExprs() {
return llvm::makeArrayRef(getTrailingObjects<Expr *>(),
NumUserSpecifiedExprs);
}

const ArrayRef<Expr *> getUserSpecifiedInitExprs() const {
return llvm::makeArrayRef(getTrailingObjects<Expr *>(),
NumUserSpecifiedExprs);
}

SourceLocation getBeginLoc() const LLVM_READONLY { return LParenLoc; }

SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; }

SourceLocation getInitLoc() const LLVM_READONLY { return InitLoc; }

SourceRange getSourceRange() const LLVM_READONLY {
return SourceRange(getBeginLoc(), getEndLoc());
}

void setArrayFiller(Expr *E) { ArrayFiller = E; }

Expr *getArrayFiller() { return ArrayFiller; }

const Expr *getArrayFiller() const { return ArrayFiller; }

child_range children() {
Stmt **Begin = reinterpret_cast<Stmt **>(getTrailingObjects<Expr *>());
return child_range(Begin, Begin + NumExprs);
}

const_child_range children() const {
Stmt *const *Begin =
reinterpret_cast<Stmt *const *>(getTrailingObjects<Expr *>());
return const_child_range(Begin, Begin + NumExprs);
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXParenListInitExprClass;
}
};

/// Represents an expression that might suspend coroutine execution;
/// either a co_await or co_yield expression.
///
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Expand Up @@ -2852,6 +2852,7 @@ DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, {})
DEF_TRAVERSE_STMT(FunctionParmPackExpr, {})
DEF_TRAVERSE_STMT(CXXFoldExpr, {})
DEF_TRAVERSE_STMT(AtomicExpr, {})
DEF_TRAVERSE_STMT(CXXParenListInitExpr, {})

DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {
if (S->getLifetimeExtendedTemporaryDecl()) {
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -2164,6 +2164,9 @@ def err_init_list_bad_dest_type : Error<
def warn_cxx20_compat_aggregate_init_with_ctors : Warning<
"aggregate initialization of type %0 with user-declared constructors "
"is incompatible with C++20">, DefaultIgnore, InGroup<CXX20Compat>;
def warn_cxx17_compat_aggregate_init_paren_list : Warning<
"aggregate initialization of type %0 from a parenthesized list of values "
"is a C++20 extension">, DefaultIgnore, InGroup<CXX20>;

def err_reference_bind_to_bitfield : Error<
"%select{non-const|volatile}0 reference cannot bind to "
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/StmtNodes.td
Expand Up @@ -160,6 +160,7 @@ def FunctionParmPackExpr : StmtNode<Expr>;
def MaterializeTemporaryExpr : StmtNode<Expr>;
def LambdaExpr : StmtNode<Expr>;
def CXXFoldExpr : StmtNode<Expr>;
def CXXParenListInitExpr: StmtNode<Expr>;

// C++ Coroutines TS expressions
def CoroutineSuspendExpr : StmtNode<Expr, 1>;
Expand Down
12 changes: 11 additions & 1 deletion clang/include/clang/Sema/Initialization.h
Expand Up @@ -923,7 +923,11 @@ class InitializationSequence {
SK_OCLSamplerInit,

/// Initialize an opaque OpenCL type (event_t, queue_t, etc.) with zero
SK_OCLZeroOpaqueType
SK_OCLZeroOpaqueType,

/// Initialize an aggreagate with parenthesized list of values.
/// This is a C++20 feature.
SK_ParenthesizedListInit
};

/// A single step in the initialization sequence.
Expand Down Expand Up @@ -1099,6 +1103,10 @@ class InitializationSequence {

/// List-copy-initialization chose an explicit constructor.
FK_ExplicitConstructor,

/// Parenthesized list initialization failed at some point.
/// This is a C++20 feature.
FK_ParenthesizedListInitFailed,
};

private:
Expand Down Expand Up @@ -1357,6 +1365,8 @@ class InitializationSequence {
/// from a zero constant.
void AddOCLZeroOpaqueTypeStep(QualType T);

void AddParenthesizedListInitStep(QualType T);

/// Add steps to unwrap a initializer list for a reference around a
/// single element and rewrap it at the end.
void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic);
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Expand Up @@ -1861,6 +1861,9 @@ enum StmtCode {
/// A CXXBoolLiteralExpr record.
EXPR_CXX_BOOL_LITERAL,

/// A CXXParenListInitExpr record.
EXPR_CXX_PAREN_LIST_INIT,

EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr
EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr).
EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type).
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/AST/ComputeDependence.cpp
Expand Up @@ -831,6 +831,13 @@ ExprDependence clang::computeDependence(CXXFoldExpr *E) {
return D;
}

ExprDependence clang::computeDependence(CXXParenListInitExpr *E) {
auto D = ExprDependence::None;
for (const auto *A : E->getInitExprs())
D |= A->getDependence();
return D;
}

ExprDependence clang::computeDependence(TypeTraitExpr *E) {
auto D = ExprDependence::None;
for (const auto *A : E->getArgs())
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Expr.cpp
Expand Up @@ -3644,6 +3644,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case ShuffleVectorExprClass:
case ConvertVectorExprClass:
case AsTypeExprClass:
case CXXParenListInitExprClass:
// These have a side-effect if any subexpression does.
break;

Expand Down
18 changes: 18 additions & 0 deletions clang/lib/AST/ExprCXX.cpp
Expand Up @@ -1757,3 +1757,21 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx,
alignof(CUDAKernelCallExpr));
return new (Mem) CUDAKernelCallExpr(NumArgs, HasFPFeatures, Empty);
}

CXXParenListInitExpr *
CXXParenListInitExpr::Create(ASTContext &C, ArrayRef<Expr *> Args, QualType T,
unsigned NumUserSpecifiedExprs,
SourceLocation InitLoc, SourceLocation LParenLoc,
SourceLocation RParenLoc) {
void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(Args.size()));
return new (Mem) CXXParenListInitExpr(Args, T, NumUserSpecifiedExprs, InitLoc,
LParenLoc, RParenLoc);
}

CXXParenListInitExpr *CXXParenListInitExpr::CreateEmpty(ASTContext &C,
unsigned NumExprs,
EmptyShell Empty) {
void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(NumExprs),
alignof(CXXParenListInitExpr));
return new (Mem) CXXParenListInitExpr(Empty, NumExprs);
}
5 changes: 5 additions & 0 deletions clang/lib/AST/ExprClassification.cpp
Expand Up @@ -445,6 +445,11 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::SYCLUniqueStableNameExprClass:
return Cl::CL_PRValue;
break;

case Expr::CXXParenListInitExprClass:
if (isa<ArrayType>(E->getType()))
return Cl::CL_ArrayTemporary;
return Cl::CL_ClassTemporary;
}

llvm_unreachable("unhandled expression kind in classification");
Expand Down

0 comments on commit 40c5215

Please sign in to comment.