261 lines
9.0 KiB
C++
261 lines
9.0 KiB
C++
//===--- ParseReflect.cpp - C++2c Reflection Parsing (P2996) --------------===//
|
|
//
|
|
// Copyright 2024 Bloomberg Finance L.P.
|
|
//
|
|
// 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/Parse/RAIIObjectsForParser.h"
|
|
#include "clang/Sema/EnterExpressionEvaluationContext.h"
|
|
using namespace clang;
|
|
|
|
ExprResult Parser::ParseCXXReflectExpression(SourceLocation OpLoc) {
|
|
SourceLocation OperandLoc = Tok.getLocation();
|
|
|
|
Sema::ConstevalOnlyRecorder RecordConstevalOnly(Actions);
|
|
EnterExpressionEvaluationContext EvalContext(
|
|
Actions, Sema::ExpressionEvaluationContext::ReflectionContext);
|
|
|
|
// Parse a leading nested-name-specifier, e.g.,
|
|
//
|
|
CXXScopeSpec SS;
|
|
if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
|
|
/*ObjectHasErrors=*/false,
|
|
/*EnteringContext=*/false)) {
|
|
SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
|
|
return ExprError();
|
|
}
|
|
|
|
// Start the tentative parse: This will be reverted if the operand is found
|
|
// to be a type (or rather: a type whose name is more complicated than a
|
|
// single identifier).
|
|
//
|
|
TentativeParsingAction TentativeAction(*this);
|
|
|
|
// Next, check for an unqualified-id.
|
|
if (Tok.isOneOf(tok::identifier, tok::kw_operator, tok::kw_template,
|
|
tok::tilde, tok::annot_template_id)) {
|
|
// Try parsing the operand name as an 'unqualified-id'.
|
|
|
|
SourceLocation TemplateKWLoc;
|
|
UnqualifiedId UnqualName;
|
|
if (!ParseUnqualifiedId(SS, ParsedType{}, /*ObjectHadError=*/false,
|
|
/*EnteringContext=*/false,
|
|
/*AllowDestructorName=*/true,
|
|
/*AllowConstructorName=*/false,
|
|
/*AllowDeductionGuide=*/false,
|
|
SS.isSet() ? &TemplateKWLoc : nullptr,
|
|
UnqualName)) {
|
|
bool AssumeType = false;
|
|
if (UnqualName.getKind() == UnqualifiedIdKind::IK_TemplateId &&
|
|
UnqualName.TemplateId->Kind == TNK_Type_template)
|
|
AssumeType = true;
|
|
else if (Tok.isOneOf(tok::l_square, tok::l_paren, tok::star, tok::amp,
|
|
tok::ampamp, tok::kw_const, tok::kw_volatile,
|
|
tok::kw_restrict))
|
|
AssumeType = true;
|
|
|
|
if (!AssumeType) {
|
|
TentativeAction.Commit();
|
|
return RecordConstevalOnly.RecordAndReturn(
|
|
Actions.ActOnCXXReflectExpr(OpLoc, TemplateKWLoc, SS,
|
|
UnqualName));
|
|
}
|
|
}
|
|
} else if (SS.isValid() &&
|
|
SS.getScopeRep()->getKind() == NestedNameSpecifier::Global) {
|
|
// Check for '^::'.
|
|
TentativeAction.Commit();
|
|
|
|
Decl *TUDecl = Actions.getASTContext().getTranslationUnitDecl();
|
|
return RecordConstevalOnly.RecordAndReturn(
|
|
Actions.ActOnCXXReflectExpr(OpLoc, SourceLocation(), TUDecl));
|
|
}
|
|
TentativeAction.Revert();
|
|
|
|
if (SS.isSet() &&
|
|
TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, true,
|
|
ImplicitTypenameContext::No)) {
|
|
SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
|
|
return ExprError();
|
|
}
|
|
|
|
// Anything else must be a type-id (e.g., 'const int', 'Cls(*)(int)'.
|
|
if (isCXXTypeId(TentativeCXXTypeIdContext::AsReflectionOperand)) {
|
|
TypeResult TR = ParseTypeName(nullptr, DeclaratorContext::ReflectOperator);
|
|
if (TR.isInvalid())
|
|
return ExprError();
|
|
|
|
std::string refKind;
|
|
if (QualType QT = cast<LocInfoType>(TR.get().get())->getType();
|
|
QT->isLValueReferenceType()) {
|
|
refKind = "&";
|
|
} else if (QT->isRValueReferenceType()) {
|
|
refKind = "&&";
|
|
} else if (auto *FPT = dyn_cast<FunctionProtoType>(QT)) {
|
|
if (FPT->getRefQualifier() == RQ_LValue)
|
|
refKind = "&";
|
|
else if (FPT->getRefQualifier() == RQ_RValue)
|
|
refKind = "&&";
|
|
}
|
|
|
|
if (!refKind.empty() &&
|
|
!Tok.isOneOf(tok::r_paren, tok::greater, tok::greatergreater,
|
|
tok::comma, tok::r_brace, tok::r_square, tok::r_splice,
|
|
tok::semi, tok::ellipsis, tok::colon, tok::question)) {
|
|
TypeLoc TL = cast<LocInfoType>(TR.get().get())
|
|
->getTypeSourceInfo()->getTypeLoc();
|
|
|
|
Diag(OperandLoc, diag::warn_meant_parenthesize_reflection)
|
|
<< refKind << TL.getSourceRange();
|
|
}
|
|
|
|
return RecordConstevalOnly.RecordAndReturn(
|
|
Actions.ActOnCXXReflectExpr(OpLoc, TR));
|
|
}
|
|
|
|
Diag(OperandLoc, diag::err_cannot_reflect_operand);
|
|
return ExprError();
|
|
}
|
|
|
|
ExprResult Parser::ParseCXXMetafunctionExpression() {
|
|
assert(Tok.is(tok::kw___metafunction) && "expected '___metafunction'");
|
|
SourceLocation KwLoc = ConsumeToken();
|
|
|
|
// Balance any number of arguments in parens.
|
|
BalancedDelimiterTracker Parens(*this, tok::l_paren);
|
|
if (Parens.expectAndConsume())
|
|
return ExprError();
|
|
|
|
SmallVector<Expr *, 2> Args;
|
|
do {
|
|
ExprResult Expr = ParseConstantExpression();
|
|
if (Expr.isInvalid()) {
|
|
Parens.skipToEnd();
|
|
return ExprError();
|
|
}
|
|
Args.push_back(Expr.get());
|
|
} while (TryConsumeToken(tok::comma));
|
|
|
|
if (Parens.consumeClose())
|
|
return ExprError();
|
|
|
|
SourceLocation LPLoc = Parens.getOpenLocation();
|
|
SourceLocation RPLoc = Parens.getCloseLocation();
|
|
return Actions.ActOnCXXMetafunction(KwLoc, LPLoc, Args, RPLoc);
|
|
}
|
|
|
|
bool Parser::ParseSpliceSpecifier(bool TryParseSpecialization) {
|
|
assert(Tok.is(tok::l_splice) && "expected '[:'");
|
|
|
|
BalancedDelimiterTracker SpliceTokens(*this, tok::l_splice);
|
|
if (SpliceTokens.expectAndConsume())
|
|
return true;
|
|
|
|
ExprResult ER = ParseConstantExpression();
|
|
if (ER.isInvalid() || ER.get()->containsErrors()) {
|
|
SpliceTokens.skipToEnd();
|
|
return true;
|
|
}
|
|
Expr *Operand = ER.get();
|
|
|
|
Token end = Tok;
|
|
if (SpliceTokens.consumeClose())
|
|
return true;
|
|
|
|
SourceLocation LSplice = SpliceTokens.getOpenLocation();
|
|
SourceLocation RSplice = SpliceTokens.getCloseLocation();
|
|
|
|
SpliceResult SR;
|
|
if (TryParseSpecialization && Tok.is(tok::less)) {
|
|
ASTTemplateArgsPtr TArgsPtr;
|
|
SourceLocation LAngleLoc, RAngleLoc;
|
|
{
|
|
TemplateArgList TArgs;
|
|
if (ParseTemplateIdAfterTemplateName(/*ConsumeLastToken=*/false,
|
|
LAngleLoc, TArgs, RAngleLoc,
|
|
/*Template=*/nullptr))
|
|
return true;
|
|
|
|
TArgsPtr = ASTTemplateArgsPtr(TArgs.data(), TArgs.size());
|
|
end = Tok;
|
|
ConsumeToken();
|
|
}
|
|
SR = Actions.ActOnSpliceSpecifier(LSplice, Operand, RSplice, LAngleLoc,
|
|
TArgsPtr, RAngleLoc);
|
|
} else {
|
|
SR = Actions.ActOnSpliceSpecifier(LSplice, Operand, RSplice);
|
|
}
|
|
if (SR.isInvalid())
|
|
return true;
|
|
SpliceSpecifier *Splice = SR.get();
|
|
|
|
UnconsumeToken(end);
|
|
Tok.setKind(tok::annot_splice);
|
|
setSpliceAnnotation(Tok, Splice);
|
|
Tok.setLocation(Splice->getBeginLoc());
|
|
Tok.setAnnotationEndLoc(Splice->getEndLoc());
|
|
PP.AnnotateCachedTokens(Tok);
|
|
|
|
return false;
|
|
}
|
|
|
|
ExprResult Parser::ParseCXXSpliceAsExpr(SourceLocation TemplateKWLoc,
|
|
bool AllowMemberReference) {
|
|
assert(Tok.is(tok::annot_splice) && "expected a splice annotation");
|
|
|
|
SpliceResult SR = getSpliceAnnotation(Tok);
|
|
if (SR.isInvalid())
|
|
return ExprError();
|
|
SpliceSpecifier *Splice = SR.get();
|
|
|
|
assert((!Splice->isSpecialization() || TemplateKWLoc.isValid()) &&
|
|
"splice-specialization-specifier required leading 'template'");
|
|
ConsumeAnnotationToken();
|
|
|
|
return Actions.ActOnCXXSpliceExpression(TemplateKWLoc, Splice,
|
|
AllowMemberReference);
|
|
}
|
|
|
|
TypeResult Parser::ParseCXXSpliceAsType(SourceLocation TypenameKWLoc,
|
|
bool AllowDependent, bool Complain) {
|
|
assert(Tok.is(tok::annot_splice) && "expected a splice annotation");
|
|
|
|
SpliceResult SR = getSpliceAnnotation(Tok);
|
|
if (SR.isInvalid())
|
|
return TypeError();
|
|
SpliceSpecifier *Splice = SR.get();
|
|
|
|
TypeResult Result = Actions.ActOnCXXSpliceTypeSpecifier(TypenameKWLoc,
|
|
Splice, Complain);
|
|
if (!Result.isInvalid())
|
|
ConsumeAnnotationToken();
|
|
|
|
return Result;
|
|
}
|
|
|
|
DeclResult Parser::ParseCXXSpliceAsNamespace() {
|
|
assert(Tok.is(tok::annot_splice) && "expected annot_splice");
|
|
|
|
SpliceResult SR = getSpliceAnnotation(Tok);
|
|
if (SR.isInvalid())
|
|
return DeclError();
|
|
SpliceSpecifier *Splice = SR.get();
|
|
|
|
assert(!Splice->isSpecialization() &&
|
|
"splice-specialization-specifier cannot represent a namespace");
|
|
ConsumeAnnotationToken();
|
|
|
|
return Actions.ActOnCXXSpliceExpectingNamespace(Splice);
|
|
}
|