Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
161 changes: 153 additions & 8 deletions CodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,97 @@ class LambdaNameOnlyCodeGenerator final : public CodeGenerator
};
//-----------------------------------------------------------------------------

/// Transforms `break` and `continue` in the instantiations of a `template for` into `goto`.
class ExpansionStmtCodeGenerator final : public CodeGenerator
{
std::string mLabelId{};
size_t mIndex{};
size_t mBreakCaptured{}; //!< Nesting depth of loops and switches capturing `break`.
size_t mContinueCaptured{}; //!< Nesting depth of loops capturing `continue`.
bool mBreakUsed{};
bool mContinueUsed{}; //!< A `switch` has no `continue`

std::string BreakLabel() const { return StrCat(BuildInternalVarName(kwBreak), mLabelId); }
std::string ContinueLabel() const { return StrCat(BuildInternalVarName(kwContinue), mLabelId, "_"sv, mIndex); }

void InsertCapturing(const Stmt* stmt, bool capturesContinue)
{
++mBreakCaptured;
if(capturesContinue) {
++mContinueCaptured;
}

CodeGenerator::InsertArg(stmt);

--mBreakCaptured;
if(capturesContinue) {
--mContinueCaptured;
}
}

public:
ExpansionStmtCodeGenerator(const CXXExpansionStmtDecl* decl,
OutputFormatHelper& outputFormatHelper,
LambdaStackType& lambdaStack)
: CodeGenerator{outputFormatHelper, lambdaStack, ProcessingPrimaryTemplate::No}
, mLabelId{MakeLineColumnName(GetSM(*decl), decl->getBeginLoc(), "_"sv)}
{
}

using CodeGenerator::InsertArg;

void InsertInstantiations(const CXXExpansionStmtInstantiation& inst)
{
for(const auto& e : inst.getPreambleStmts()) {
InsertArg(e);
}

for(const auto e : inst.getInstantiations()) {
mContinueUsed = false;

InsertArg(e);
mOutputFormatHelper.AppendNewLine();

if(mContinueUsed) {
mOutputFormatHelper.AppendNewLine(ContinueLabel(), ":"sv);
}

++mIndex;
}

if(mBreakUsed) {
mOutputFormatHelper.AppendNewLine(BreakLabel(), ":"sv);
}
}

void InsertArg(const BreakStmt* stmt) override
{
if(mBreakCaptured) {
CodeGenerator::InsertArg(stmt);
} else {
mBreakUsed = true;
mOutputFormatHelper.Append(kwGotoSpace, BreakLabel());
}
}

void InsertArg(const ContinueStmt* stmt) override
{
if(mContinueCaptured) {
CodeGenerator::InsertArg(stmt);
} else {
mContinueUsed = true;
mOutputFormatHelper.Append(kwGotoSpace, ContinueLabel());
}
}

void InsertArg(const ForStmt* stmt) override { InsertCapturing(stmt, true); }
void InsertArg(const CXXForRangeStmt* stmt) override { InsertCapturing(stmt, true); }
void InsertArg(const WhileStmt* stmt) override { InsertCapturing(stmt, true); }
void InsertArg(const DoStmt* stmt) override { InsertCapturing(stmt, true); }
void InsertArg(const SwitchStmt* stmt) override { InsertCapturing(stmt, false); }
};
//-----------------------------------------------------------------------------

CodeGenerator::LambdaScopeHandler::LambdaScopeHandler(LambdaStackType& stack,
OutputFormatHelper& outputFormatHelper,
const LambdaCallerType lambdaCallerType)
Expand Down Expand Up @@ -825,7 +916,8 @@ static std::string_view GetStorageClassAsString(const StorageClass& sc)

static std::string GetStorageClassAsStringWithSpace(const StorageClass& sc)
{
std::string ret{GetStorageClassAsString(sc)};
// `auto` as a storage class specifier is not valid C++. Expansion statements synthesize such declarations.
std::string ret{SC_Auto == sc ? ""sv : GetStorageClassAsString(sc)};

if(not ret.empty()) {
ret.append(" "sv);
Expand Down Expand Up @@ -945,11 +1037,11 @@ class TemporaryDeclFinder : public StmtVisitor<TemporaryDeclFinder>
// XXX hack. Our normal VarDecl is at TU level. It then appears in cxa_start...
auto& ctx = GetGlobalAST();
auto* vd = ImplicitParamDecl::Create(const_cast<ASTContext&>(ctx),
ctx.getTranslationUnitDecl(),
ctx.getTranslationUnitDecl(),
{},
&ctx.Idents.get(mTempName),
expr->getType(),
ImplicitParamKind::Other);
&ctx.Idents.get(mTempName),
expr->getType(),
ImplicitParamKind::Other);

#endif

Expand Down Expand Up @@ -1425,7 +1517,11 @@ void CodeGenerator::InsertArg(const VarDecl* stmt)
IsPrimaryTemplatePackExpansionExpr(dyn_cast_or_null<ParenListExpr>(init))};

if(not isa<CXXParenListInitExpr>(init) and not isPrimaryTemplatePackExpansionExpr) {
mOutputFormatHelper.Append(hlpAssing);
if(ReplaceAssignWithColon::No == mReplaceAssignWithColon) {
mOutputFormatHelper.Append(hlpAssing);
} else {
mOutputFormatHelper.Append(" : ");
}
}

WrapInParensIfNeeded(isPrimaryTemplatePackExpansionExpr, [&] {
Expand Down Expand Up @@ -2085,6 +2181,52 @@ void CodeGenerator::InsertArg(const CXXFoldExpr* stmt)
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertArg(const CXXExpansionSelectExpr* stmt)
{
InsertArg(stmt->getRangeExpr());
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertArg(const CXXExpansionStmtDecl* stmt)
{
if(const auto* inst = stmt->getInstantiations()) {
mOutputFormatHelper.OpenScope();

ExpansionStmtCodeGenerator codeGenerator{stmt, mOutputFormatHelper, mLambdaStack};
codeGenerator.InsertInstantiations(*inst);

mOutputFormatHelper.CloseScope();

} else if(const auto* pattern = stmt->getExpansionPattern()) {
mOutputFormatHelper.Append(kwTemplate, " for"sv);

WrapInParens([&] {
if(const auto* init = pattern->getInit()) {
MultiStmtDeclCodeGenerator codeGenerator{mOutputFormatHelper, mLambdaStack, true};
codeGenerator.InsertArg(init);
}

MultiStmtDeclCodeGenerator codeGenerator{
mOutputFormatHelper, mLambdaStack, MultiStmtDeclCodeGenerator::SkipFinalSemi::Yes};

{
BackupAndRestore _{codeGenerator.mReplaceAssignWithColon, ReplaceAssignWithColon::Yes};
codeGenerator.InsertArg(pattern->getExpansionVarStmt());
}

if(pattern->isDependent()) {
mOutputFormatHelper.Append(" : "sv);
InsertArg(pattern->getExpansionInitializer());
}
});

InsertArg(pattern->getBody());
}

mOutputFormatHelper.AppendNewLine();
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertArg(const PackIndexingExpr* stmt)
{
if(stmt->isFullySubstituted()) {
Expand Down Expand Up @@ -2498,9 +2640,12 @@ void CodeGenerator::HandleCompoundStmt(const CompoundStmt* stmt)
CXXForRangeStmt,
SwitchStmt,
CXXTryStmt,
CompoundStmt,
CppInsightsCommentStmt>(item) and
InsertSemi() and not skipSemiForLambda and not mSkipSemi) {
mOutputFormatHelper.AppendSemiNewLine();
} else if(isa<CompoundStmt>(item)) {
mOutputFormatHelper.AppendNewLine();
}

mSkipSemi = false;
Expand Down Expand Up @@ -4306,7 +4451,7 @@ void CodeGenerator::FormatCast(const std::string_view castName,
const CastKind& castKind)
{
const bool isCastToBase{is{castKind}.any_of(CK_DerivedToBase, CK_UncheckedDerivedToBase) and
castDestType->isRecordType()};
castDestType->isRecordType()};
const std::string castDestTypeText{
StrCat(GetName(castDestType), ((isCastToBase and not castDestType->isAnyPointerType()) ? "&"sv : ""sv))};

Expand Down Expand Up @@ -4724,7 +4869,7 @@ void CodeGenerator::InsertFunctionNameWithReturnType(const FunctionDecl& d
bool isCXXMethodDecl{nullptr != methodDecl};
const bool isClassTemplateSpec{isCXXMethodDecl and isa<ClassTemplateSpecializationDecl>(methodDecl->getParent())};
const bool requiresComment{isCXXMethodDecl and not methodDecl->isUserProvided() and
not methodDecl->isExplicitlyDefaulted()};
not methodDecl->isExplicitlyDefaulted()};
// [expr.prim.lambda.closure] p7 consteval/constexpr are obtained from the call operator
const bool isLambdaStaticInvoker{isCXXMethodDecl and methodDecl->isLambdaStaticInvoker()};
const auto desugaredReturnType = GetType(GetDesugarReturnType(decl));
Expand Down
30 changes: 25 additions & 5 deletions CodeGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ class CodeGenerator
STRONG_BOOL(UseCommaInsteadOfSemi);
STRONG_BOOL(NoEmptyInitList);
STRONG_BOOL(ShowConstantExprValue);
STRONG_BOOL(ReplaceAssignWithColon);

ShowConstantExprValue mShowConstantExprValue{ShowConstantExprValue::No};
SkipVarDecl mSkipVarDecl{SkipVarDecl::No};
Expand All @@ -430,11 +431,12 @@ class CodeGenerator
//!< potential std::initializer_list expansion must be inserted.
std::optional<size_t> mCurrentFieldPos{}; //!< The position in mOutputFormatHelper in a class where where a
//!< potential std::initializer_list expansion must be inserted.
OutputFormatHelper* mOutputFormatHelperOutside{
OutputFormatHelper* mOutputFormatHelperOutside{
nullptr}; //!< Helper output buffer for std::initializer_list expansion.
bool mRequiresImplicitReturnZero{}; //!< Track whether this is a function with an imlpicit return 0.
bool mSkipSemi{};
ProcessingPrimaryTemplate mProcessingPrimaryTemplate{};
ReplaceAssignWithColon mReplaceAssignWithColon{}; //!< Special case for `for template`.
static inline std::map<std::string, bool> mSeenDecls{};
};
//-----------------------------------------------------------------------------
Expand All @@ -460,15 +462,33 @@ class MultiStmtDeclCodeGenerator final : public CodeGenerator
{
}

STRONG_BOOL(SkipFinalSemi);

explicit MultiStmtDeclCodeGenerator(OutputFormatHelper& _outputFormatHelper,
LambdaStackType& lambdaStack,
SkipFinalSemi skipFinalSemi)
: CodeGenerator{_outputFormatHelper, lambdaStack, ProcessingPrimaryTemplate::No}
, mInsertVarDecl{true}
, mInsertComma{}
, mSkipFinalSemi{skipFinalSemi}
{
}

// Insert the semi after the last declaration. This implies that this class always requires its own scope.
~MultiStmtDeclCodeGenerator() { mOutputFormatHelper.Append("; "sv); }
~MultiStmtDeclCodeGenerator()
{
if(SkipFinalSemi::No == mSkipFinalSemi) {
mOutputFormatHelper.Append("; "sv);
}
}

using CodeGenerator::InsertArg;

protected:
OnceTrue mInsertVarDecl{}; //! Insert the \c VarDecl only once.
OnceFalse mInsertComma{}; //! Insert the comma after we have generated the first \c VarDecl and we are about to
//! insert another one.
OnceTrue mInsertVarDecl{}; //! Insert the \c VarDecl only once.
OnceFalse mInsertComma{}; //! Insert the comma after we have generated the first \c VarDecl and we are about to
//! insert another one.
SkipFinalSemi mSkipFinalSemi{SkipFinalSemi::No}; //! Skip inserting the semi colon once the object gets destroyed.

bool InsertVarDecl(const VarDecl*) override { return mInsertVarDecl; }
bool InsertComma() override { return mInsertComma; }
Expand Down
2 changes: 2 additions & 0 deletions CodeGeneratorTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ SUPPORTED_DECL(VarTemplateDecl)
SUPPORTED_DECL(ConceptDecl)
SUPPORTED_DECL(UnresolvedUsingValueDecl)
SUPPORTED_DECL(LinkageSpecDecl)
SUPPORTED_DECL(CXXExpansionStmtDecl)

SUPPORTED_STMT(CXXDeleteExpr)
SUPPORTED_STMT(CXXDefaultInitExpr)
Expand Down Expand Up @@ -140,6 +141,7 @@ SUPPORTED_STMT(CXXParenListInitExpr)
SUPPORTED_STMT(CppInsightsCommentStmt)
SUPPORTED_STMT(CXXPseudoDestructorExpr)
SUPPORTED_STMT(PackIndexingExpr)
SUPPORTED_STMT(CXXExpansionSelectExpr)

#undef IGNORED_DECL
#undef IGNORED_STMT
Expand Down
8 changes: 4 additions & 4 deletions InsightsHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,10 +486,10 @@ class SimpleTypePrinter
std::string mDataAfter{};
bool mHasData{false};
bool mSkipSpace{false};
bool mScanningArrayDimension{}; //!< Only the outer most ConstantArrayType handles the array dimensions and size
std::string mScope{}; //!< A scope coming from an ElaboratedType which is used for a
//!< ClassTemplateSpecializationDecl if there is no other scope
bool mTmplParamTypeRes{}; //!< When processing FunctionProtoType parameters we need to get the identifiers
bool mScanningArrayDimension{}; //!< Only the outer most ConstantArrayType handles the array dimensions and size
std::string mScope{}; //!< A scope coming from an ElaboratedType which is used for a
//!< ClassTemplateSpecializationDecl if there is no other scope
bool mTmplParamTypeRes{}; //!< When processing FunctionProtoType parameters we need to get the identifiers

bool HandleType(const TemplateTypeParmType* type)
{
Expand Down
4 changes: 2 additions & 2 deletions tests/Issue540.expect
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ int main()
{
int a = 5;
std::cout.operator<<(a + (::a));
};
};
}
}
return 0;
}
2 changes: 1 addition & 1 deletion tests/StructuredBindingsHandler3Test.expect
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ namespace constant
int && b = get<1UL>(static_cast<Q &&>(__q26));
int && c = get<2UL>(static_cast<Q &&>(__q26));
p = &c;
};
}
return *p;
}

Expand Down
10 changes: 10 additions & 0 deletions tests/p1306Test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// cmdline:-std=c++26

int main()
{
int res{};
template for(auto x : {1, 2, 3})
{
res += x;
}
}
26 changes: 26 additions & 0 deletions tests/p1306Test.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
int main()
{
int res = {};
{
{
int x = 1;
{
res = res + x;
}
}
{
int x = 2;
{
res = res + x;
}
}
{
int x = 3;
{
res = res + x;
}
}

}
return 0;
}
25 changes: 25 additions & 0 deletions tests/p1306Test_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// cmdline:-std=c++26

#define INSIGHTS_USE_TEMPLATE 1

template<typename T,
typename... Args // #A Variadic template
>
constexpr T min(const T& x,
const T& y,
const Args&... vals) // #B Parameter pack
{
auto m = x < y ? x : y;

template for(int init{3}; const auto& e : {vals...})
{
if(e < m) { m = e; }
}

return m;
}

static_assert(min(3, 2) == 2);
static_assert(min(3, 2, 3, 4, 5) == 2);

int main() {}
Loading
Loading