Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
cda974f
initial commit
changkhothuychung Dec 22, 2024
daf965a
Merge pull request #2 from changkhothuychung/concat
changkhothuychung Dec 22, 2024
5cfcf49
Revert "implement views::concat (P2542) "
changkhothuychung Dec 22, 2024
59c609c
Merge pull request #3 from changkhothuychung/revert-2-concat
changkhothuychung Dec 22, 2024
c59b1d3
Merge branch 'main' of https://github.com/llvm/llvm-project
changkhothuychung Dec 24, 2024
a9df861
Merge branch 'main' of https://github.com/llvm/llvm-project
changkhothuychung Jan 8, 2025
9b353b7
Merge branch 'main' of https://github.com/llvm/llvm-project
changkhothuychung Feb 9, 2025
88d8af8
Merge branch 'main' of https://github.com/llvm/llvm-project
changkhothuychung Apr 18, 2025
245f466
Merge branch 'main' of https://github.com/llvm/llvm-project
changkhothuychung Sep 26, 2025
482e25e
parsing global namespace and primitive types
changkhothuychung Oct 22, 2025
c2d6d7e
cleanup
changkhothuychung Oct 22, 2025
7c7ac79
cleanup
changkhothuychung Oct 22, 2025
61c7f94
cleanup
changkhothuychung Oct 22, 2025
78fe7c4
cleanup
changkhothuychung Oct 22, 2025
5ae0873
add dummy mangling
changkhothuychung Oct 22, 2025
4503b11
add file description
changkhothuychung Oct 22, 2025
4852569
clang format
changkhothuychung Oct 22, 2025
d3f0f70
clang format
changkhothuychung Oct 22, 2025
e93fa6e
clang format
changkhothuychung Oct 22, 2025
e845cb8
add CXXReflectExprClass to Unexposed case for now
changkhothuychung Oct 22, 2025
2d8c1cb
enum for CXXReflectExprClass
changkhothuychung Oct 22, 2025
7a331be
unused var
changkhothuychung Oct 22, 2025
340d7c0
remove wrong assertion
changkhothuychung Oct 22, 2025
7c30c5d
synthesize TypeSourceInfo before passing into ActOnReflectExpr
changkhothuychung Oct 22, 2025
08cd161
c++26 for test file instead of c++23
changkhothuychung Oct 22, 2025
258283e
change var name to reflect consuming ^^
changkhothuychung Oct 22, 2025
6b8aaf5
fix syntax
changkhothuychung Oct 22, 2025
8660059
change to use void for type placeholder
changkhothuychung Oct 22, 2025
e6fec7b
improve description of class CXXReflectExpr
changkhothuychung Oct 22, 2025
95ec04c
clang format
changkhothuychung Oct 22, 2025
8be44be
description update
changkhothuychung Oct 22, 2025
5af4ae2
Update clang/include/clang/Driver/Options.td
changkhothuychung Oct 22, 2025
13cbe6f
use OperandLoc instead
changkhothuychung Oct 22, 2025
8bd9dc3
remove IsNamespace
changkhothuychung Oct 22, 2025
dacc226
remove feature flag
changkhothuychung Oct 23, 2025
442feeb
add more test cases and rename test file
changkhothuychung Oct 23, 2025
ad7a5e6
change diagnostics description
changkhothuychung Oct 23, 2025
632c4d8
accept only built-in types for now, no other type-id yet
changkhothuychung Oct 23, 2025
ae941f2
clang format
changkhothuychung Oct 23, 2025
745cc22
fix error message in test
changkhothuychung Oct 23, 2025
f61f630
leave this function for later
changkhothuychung Oct 23, 2025
455549f
leave StmtPrinter::VisitCXXReflectExpr for later
changkhothuychung Oct 23, 2025
a7281de
update note for TODO(Reflection)
changkhothuychung Oct 23, 2025
72c191d
Update clang/include/clang/AST/ExprCXX.h
changkhothuychung Oct 24, 2025
af47462
Merge branch 'main' of https://github.com/changkhothuychung/llvm-project
changkhothuychung Oct 24, 2025
21b9784
Merge branch 'refl-parse' of https://github.com/changkhothuychung/llv…
changkhothuychung Oct 24, 2025
0de3284
clang format
changkhothuychung Oct 24, 2025
634c3dd
revert unintedned change
changkhothuychung Oct 24, 2025
dc5b7cc
remove unintended new line
changkhothuychung Oct 24, 2025
5a4efbd
add c++26 check when parsing double caret
changkhothuychung Oct 24, 2025
ceafe4b
leave note for TODO
changkhothuychung Oct 24, 2025
0ac0294
add note for TODO
changkhothuychung Oct 24, 2025
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
54 changes: 54 additions & 0 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -5501,6 +5501,60 @@ class BuiltinBitCastExpr final
}
};

/// Represents a C++26 reflect expression [expr.reflect]. The operand of of the
/// expression is either a:
/// - :: (global namespace)
/// - a reflection-name
/// - a type-id
/// - id-expression.
Comment on lines +5504 to +5509
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Represents a C++26 reflect expression [expr.reflect]. The operand of of the
/// expression is either a:
/// - :: (global namespace)
/// - a reflection-name
/// - a type-id
/// - id-expression.
/// Represents a C++26 reflect expression [expr.reflect]. The operand of the
/// expression is either:
/// - :: (global namespace),
/// - a reflection-name,
/// - a type-id, or
/// - an id-expression.

class CXXReflectExpr : public Expr {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you document this better? This comment seems outdated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated, does it look ok now?


// Source locations.
SourceLocation OperatorLoc;
SourceRange OperandRange;

CXXReflectExpr(const ASTContext &C, QualType T, QualType Ty);
CXXReflectExpr(const ASTContext &C, QualType T, Decl *Arg);
CXXReflectExpr(EmptyShell Empty);

public:
static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc,
SourceLocation ArgLoc, QualType Operand);

static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between these two create functions, and when would we use one vs the other?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, the top one I create to use for parsing builtin types, it may be extended to parse other type-id's in upcoming PRs. The other one I use for parsing global namespace for now, but I think it could be extended in upcoming PRs for other kinds, e.g, namespace name.

SourceLocation OperandLoc, Decl *Operand);

static CXXReflectExpr *CreateEmpty(ASTContext &C);

SourceLocation getBeginLoc() const LLVM_READONLY { return OperatorLoc; }
SourceLocation getEndLoc() const LLVM_READONLY {
return OperandRange.getEnd();
}
SourceRange getSourceRange() const {
return SourceRange(getBeginLoc(), getEndLoc());
}

/// Returns location of the '^^'-operator.
SourceLocation getOperatorLoc() const { return OperatorLoc; }
SourceRange getOperandRange() const { return OperandRange; }

/// Sets the location of the '^^'-operator.
void setOperatorLoc(SourceLocation L) { OperatorLoc = L; }
void setOperandRange(SourceRange R) { OperandRange = R; }

child_range children() {
return child_range(child_iterator(), child_iterator());
}

const_child_range children() const {
return const_child_range(const_child_iterator(), const_child_iterator());
}

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

} // namespace clang

#endif // LLVM_CLANG_AST_EXPRCXX_H
2 changes: 2 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2883,6 +2883,8 @@ DEF_TRAVERSE_STMT(CXXUnresolvedConstructExpr, {
TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
})

DEF_TRAVERSE_STMT(CXXReflectExpr, {/*TODO*/})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be mis-remembering, but I think this is used by TreeTransform, in which case we might need this filled in for something like

template <typename T>
constexpr int r = (^^T, 1);

to work properly. Might be worth implementing this in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From your example if looks like we need support for reflection of template right? but for now this PR only supports global namespace and primitive types. If missing this might cause some issues for the internals, let me know. I can look into it as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite - r is the template; T is a type that's dependent on a template parameter. During instantiation of e.g., r<int>, TreeTransform will walk down the recursive structure of r, substituting every T with int, which will result in ^^int. The implementation of this function defines that recursive structure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEF_TRAVERSE_STMT needs to explicitly visit substatements that are not already included in Stmt::children(), so for a lot of nodes this can just be empty, though if the reflexion is a type, make sure that it traverses the TypeLoc of that type; I don’t think that can be done automatically.

TreeTransform doesn’t use RAV; it’s its own thing; that needs to be implemented separately in clang/lib/Sema/TreeTransform.h

Copy link
Contributor

@katzdm katzdm Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TreeTransform doesn't use RAV

Whoops! Thanks for the correction. Though we probably still want to recurse over that TypeLoc in TreeTransform to handle things like the above, right?

Copy link
Member

@Sirraide Sirraide Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though we probably still want to recurse over that TypeLoc in TreeTransform to handle things like the above, right?

TreeTransform needs to instantiate the type if the reflexion has a type operand in TransformCXXReflectExpr(), yes, but that’s a rather manual process (i.e. checking if the reflexion is a type and then calling getDerived().TransformType()).

I haven’t thought about this too much, but the logic will probably end up being fairly similar to TransformUnaryExprOrTypeTraitExpr(), because those can also have either expressions or types as an argument.


// These expressions all might take explicit template arguments.
// We traverse those if so. FIXME: implement these.
DEF_TRAVERSE_STMT(CXXConstructExpr, {})
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,11 @@ def err_placeholder_expected_auto_or_decltype_auto : Error<
"expected 'auto' or 'decltype(auto)' after concept name">;
}

let CategoryName = "Reflection Issue" in {
def err_cannot_reflect_operand : Error<
"expected reflectable entity">;
}

def warn_max_tokens : Warning<
"the number of preprocessor source tokens (%0) exceeds this token limit (%1)">,
InGroup<MaxTokens>, DefaultIgnore;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ LANGOPT(BoundsSafety, 1, 0, NotCompatible, "Bounds safety extension for C")
LANGOPT(EnableLifetimeSafety, 1, 0, NotCompatible, "Experimental lifetime safety analysis for C++")

LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector type")
LANGOPT(Reflection , 1, 0, NotCompatible, "C++26 Reflection")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want this as an option. IMO, we should just have this be turned on for C++26, and not enable-able for other language versions.


#undef LANGOPT
#undef ENUM_LANGOPT
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ def CoyieldExpr : StmtNode<CoroutineSuspendExpr>;
def ConceptSpecializationExpr : StmtNode<Expr>;
def RequiresExpr : StmtNode<Expr>;

// c++ 26 reflection
def CXXReflectExpr : StmtNode<Expr>;

// Obj-C Expressions.
def ObjCStringLiteral : StmtNode<Expr>;
def ObjCBoxedExpr : StmtNode<Expr>;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ PUNCTUATOR(greatergreater, ">>")
PUNCTUATOR(greaterequal, ">=")
PUNCTUATOR(greatergreaterequal, ">>=")
PUNCTUATOR(caret, "^")
PUNCTUATOR(caretcaret, "^^")
PUNCTUATOR(caretequal, "^=")
PUNCTUATOR(pipe, "|")
PUNCTUATOR(pipepipe, "||")
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3689,6 +3689,11 @@ defm application_extension : BoolFOption<"application-extension",
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Restrict code to those available for App Extensions">,
NegFlag<SetFalse>>;
defm reflection : BoolFOption<"reflection",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, we should decide which versions we want this enabled for, but I don't think just enabling on a command line arg is going to be our direction forward.

LangOpts<"Reflection">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Enable C++26 reflection">,
NegFlag<SetFalse>>;
defm sized_deallocation : BoolFOption<"sized-deallocation",
LangOpts<"SizedDeallocation">, Default<cpp14.KeyPath>,
PosFlag<SetTrue, [], [], "Enable C++14 sized global deallocation functions">,
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ enum class TentativeCXXTypeIdContext {
AsTemplateArgument,
InTrailingReturnType,
AsGenericSelectionArgument,
AsReflectionOperand
};

/// The kind of attribute specifier we have found.
Expand Down Expand Up @@ -5167,6 +5168,10 @@ class Parser : public CodeCompletionHandler {
/// Implementations are in ParseHLSL.cpp
///@{

//===--------------------------------------------------------------------===//
// Parses the operand of reflection operator
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have a \param and \return

ExprResult ParseCXXReflectExpression(SourceLocation OpLoc);

private:
bool MaybeParseHLSLAnnotations(Declarator &D,
SourceLocation *EndLoc = nullptr,
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -14756,6 +14756,16 @@ class Sema final : public SemaBase {
/// Implementations are in SemaConcept.cpp
///@{

public:
ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, TypeSourceInfo *T);
ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, SourceLocation ArgLoc,
Decl *D);

ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc,
SourceLocation OperandLoc, QualType T);
ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc,
SourceLocation OperandLoc, Decl *D);

public:
void PushSatisfactionStackEntry(const NamedDecl *D,
const llvm::FoldingSetNodeID &ID) {
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1925,6 +1925,9 @@ enum StmtCode {
EXPR_CONCEPT_SPECIALIZATION, // ConceptSpecializationExpr
EXPR_REQUIRES, // RequiresExpr

// Reflection
EXPR_REFLECT,

// CUDA
EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3730,6 +3730,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case PackIndexingExprClass:
case HLSLOutArgExprClass:
case OpenACCAsteriskSizeExprClass:
case CXXReflectExprClass:
// These never have a side-effect.
return false;

Expand Down
34 changes: 34 additions & 0 deletions clang/lib/AST/ExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1939,6 +1939,40 @@ TypeTraitExpr *TypeTraitExpr::CreateDeserialized(const ASTContext &C,
return new (Mem) TypeTraitExpr(EmptyShell(), IsStoredAsBool);
}

CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, QualType Ty)
: Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary) {}

CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, Decl *Arg)
: Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary) {}

CXXReflectExpr::CXXReflectExpr(EmptyShell Empty)
: Expr(CXXReflectExprClass, Empty) {}

CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C,
SourceLocation OperatorLoc,
SourceLocation OperandLoc,
QualType Operand) {
CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.VoidTy, Operand);
E->setOperatorLoc(OperatorLoc);
E->setOperandRange(OperandLoc);
return E;
}

CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C,
SourceLocation OperatorLoc,
SourceLocation OperandLoc,
Decl *Operand) {
CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.VoidTy, Operand);

E->setOperatorLoc(OperatorLoc);
E->setOperandRange(OperandLoc);
return E;
}

CXXReflectExpr *CXXReflectExpr::CreateEmpty(ASTContext &C) {
return new (C) CXXReflectExpr(EmptyShell());
}

CUDAKernelCallExpr::CUDAKernelCallExpr(Expr *Fn, CallExpr *Config,
ArrayRef<Expr *> Args, QualType Ty,
ExprValueKind VK, SourceLocation RP,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ExprClassification.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::SourceLocExprClass:
case Expr::ConceptSpecializationExprClass:
case Expr::RequiresExprClass:
case Expr::CXXReflectExprClass:
return Cl::CL_PRValue;

case Expr::EmbedExprClass:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19072,6 +19072,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::ArrayTypeTraitExprClass:
case Expr::ExpressionTraitExprClass:
case Expr::CXXNoexceptExprClass:
case Expr::CXXReflectExprClass:
return NoDiag();
case Expr::CallExprClass:
case Expr::CXXOperatorCallExprClass: {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4946,6 +4946,12 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
E = cast<ConstantExpr>(E)->getSubExpr();
goto recurse;

case Expr::CXXReflectExprClass: {
// TODO(Reflection): implement this after introducing std::meta::info
// and add info in APValue
break;
}

// FIXME: invent manglings for all these.
case Expr::BlockExprClass:
case Expr::ChooseExprClass:
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2566,6 +2566,11 @@ void StmtPrinter::VisitCXXUnresolvedConstructExpr(
OS << ')';
}

void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) {
// TODO(Reflection): Implement this.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think you could partially implement this but I guess you are avoiding it to keep things simple?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah im thinking since we don't have enough info in CXXReflectExpr yet, we can leave it for the next round. But let me know what you think we can show now.

llvm_unreachable("not implemented yet");
}

void StmtPrinter::VisitCXXDependentScopeMemberExpr(
CXXDependentScopeMemberExpr *Node) {
if (!Node->isImplicitAccess()) {
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2167,6 +2167,11 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) {
ID.AddInteger(Hasher.CalculateHash());
}

void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) {
// TODO(Reflection): Implement this.
llvm_unreachable("not implemented yet");
}

void
StmtProfiler::VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *S) {
VisitExpr(S);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Lex/Lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4348,6 +4348,9 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
if (Char == '=') {
CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
Kind = tok::caretequal;
} else if (LangOpts.Reflection && LangOpts.CPlusPlus26 && Char == '^') {
CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
Kind = tok::caretcaret;
} else {
if (LangOpts.OpenCL && Char == '^')
Diag(CurPtr, diag::err_opencl_logical_exclusive_or);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Parse/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ add_clang_library(clangParse
ParseTentative.cpp
Parser.cpp
ParseOpenACC.cpp
ParseReflect.cpp

LINK_LIBS
clangAST
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,13 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand,
AllowSuffix = false;
Res = ParseUnaryExprOrTypeTraitExpression();
break;
case tok::caretcaret: {
if (getLangOpts().Reflection) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Else case here feels like a bad idea?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add a diagnostic here in the else case about option flag for reflection not turned on, But you left a comment above about whether we need to have an option flag for reflection, so I guess lets resolve that first.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you also need the changes to ParseRHSOfBinaryExpr: That's where we "reinsert" two ^-tokens if blocks are enabled. Otherwise, the lexing of caretcaret becomes a breaking change for blocks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you distinguish a block though? Maybe we don;t want to do arbitrary lookahead to see if we find a brace everytime someone uses ^^

I think for the time being I would prefer

  • ^^ is only lexed as one token in C++2c mode
  • It is always considered to be a splice

We can figure out recovery for objective c++ later (and maybe always propose a fixit to inject a space)

@AaronBallman @erichkeane

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for lookahead afaict - if you're in a binary expression, it can't be a reflect-expression.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t really know much Objective-C, but I would imagine that an xor of an immediately-invoked block wouldn’t be too common, and if someone wants that parse, they can still write ^(^ { ... } ()), so it might be fine to just always treat ^^ as a reflexion in C++ mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just following up, are we going to need to handle the case for block literal in ParseRHSOfBinaryExpression ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I apologize but we should add tests in Objective-C to make sure we don't break things accidently. Maybe @rjmccall might be helpful here in guiding us on things to look out for here.

SourceLocation DoubleCaret = ConsumeToken();
Res = ParseCXXReflectExpression(/*OpLoc=*/DoubleCaret);
}
break;
}
case tok::ampamp: { // unary-expression: '&&' identifier
if (NotPrimaryExpression)
*NotPrimaryExpression = true;
Expand Down Expand Up @@ -2249,6 +2256,9 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
else if (getLangOpts().C2y && OpTok.is(tok::kw__Countof))
Diag(OpTok, diag::warn_c2y_compat_keyword) << OpTok.getName();

if (OpTok.is(tok::caretcaret))
return ParseCXXReflectExpression(OpTok.getLocation());

EnterExpressionEvaluationContext Unevaluated(
Actions, Sema::ExpressionEvaluationContext::Unevaluated,
Sema::ReuseLambdaContextDecl);
Expand Down
61 changes: 61 additions & 0 deletions clang/lib/Parse/ParseReflect.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//===--- ParseReflect.cpp - C++26 Reflection Parsing ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements parsing for reflection facilities.
//
//===----------------------------------------------------------------------===//

#include "clang/AST/LocInfoType.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Parse/Parser.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
using namespace clang;

ExprResult Parser::ParseCXXReflectExpression(SourceLocation OpLoc) {
// TODO(reflection) : support parsing for more reflect-expressions.
EnterExpressionEvaluationContext Unevaluated(
Actions, Sema::ExpressionEvaluationContext::Unevaluated);

SourceLocation OperandLoc = Tok.getLocation();

{
TentativeParsingAction TPA(*this);
// global namespace ::
if (Tok.is(tok::coloncolon)) {
ConsumeToken();
TPA.Commit();
Decl *TUDecl = Actions.getASTContext().getTranslationUnitDecl();
return Actions.ActOnCXXReflectExpr(OpLoc, SourceLocation(), TUDecl);
}
TPA.Revert();
}

if (isCXXTypeId(TentativeCXXTypeIdContext::AsReflectionOperand)) {
TypeResult TR = ParseTypeName(/*TypeOf=*/nullptr);
if (TR.isInvalid())
return ExprError();

TypeSourceInfo *TSI = nullptr;
QualType QT = Actions.GetTypeFromParser(TR.get(), &TSI);

if (QT.isNull())
return ExprError();

if (!TSI)
TSI = Actions.getASTContext().getTrivialTypeSourceInfo(QT, OperandLoc);

QualType Canon = QT.getCanonicalType();
if (Canon->isBuiltinType()) {
// Only supports builtin types for now
return Actions.ActOnCXXReflectExpr(OpLoc, TSI);
}
}

Diag(OperandLoc, diag::err_cannot_reflect_operand);
return ExprError();
}
3 changes: 3 additions & 0 deletions clang/lib/Parse/ParseTentative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,9 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
} else if (Context == TentativeCXXTypeIdContext::InTrailingReturnType) {
TPR = TPResult::True;
isAmbiguous = true;
} else if (Context == TentativeCXXTypeIdContext::AsReflectionOperand) {
TPR = TPResult::True;
isAmbiguous = true;
} else
TPR = TPResult::False;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaExceptionSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
case Expr::CXXNoexceptExprClass:
case Expr::CXXNullPtrLiteralExprClass:
case Expr::CXXPseudoDestructorExprClass:
case Expr::CXXReflectExprClass:
case Expr::CXXScalarValueInitExprClass:
case Expr::CXXThisExprClass:
case Expr::CXXUuidofExprClass:
Expand Down
Loading