|
24 | 24 | using namespace clang; |
25 | 25 | using namespace sema; |
26 | 26 |
|
| 27 | +// Build a 'DeclRefExpr' designating the template parameter '__N'. |
| 28 | +static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) { |
| 29 | + return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(), |
| 30 | + S.Context.getPointerDiffType(), VK_PRValue, |
| 31 | + ESD->getBeginLoc()); |
| 32 | +} |
| 33 | + |
| 34 | +static bool FinaliseExpansionVar(Sema &S, VarDecl *ExpansionVar, |
| 35 | + ExprResult Initializer) { |
| 36 | + if (Initializer.isInvalid()) { |
| 37 | + S.ActOnInitializerError(ExpansionVar); |
| 38 | + return true; |
| 39 | + } |
| 40 | + |
| 41 | + S.AddInitializerToDecl(ExpansionVar, Initializer.get(), /*DirectInit=*/false); |
| 42 | + return ExpansionVar->isInvalidDecl(); |
| 43 | +} |
27 | 44 |
|
28 | 45 | CXXExpansionStmtDecl * |
29 | 46 | Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth, |
@@ -69,13 +86,147 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern( |
69 | 86 | Expr *ExpansionInitializer, SourceLocation LParenLoc, |
70 | 87 | SourceLocation ColonLoc, SourceLocation RParenLoc, |
71 | 88 | ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) { |
| 89 | + if (!ExpansionInitializer || !ExpansionVarStmt) |
| 90 | + return StmtError(); |
| 91 | + |
| 92 | + assert(CurContext->isExpansionStmt()); |
| 93 | + auto *DS = cast<DeclStmt>(ExpansionVarStmt); |
| 94 | + if (!DS->isSingleDecl()) { |
| 95 | + Diag(DS->getBeginLoc(), diag::err_type_defined_in_for_range); |
| 96 | + return StmtError(); |
| 97 | + } |
| 98 | + |
| 99 | + VarDecl *ExpansionVar = dyn_cast<VarDecl>(DS->getSingleDecl()); |
| 100 | + if (!ExpansionVar || ExpansionVar->isInvalidDecl() || |
| 101 | + ExpansionInitializer->containsErrors()) |
| 102 | + return StmtError(); |
| 103 | + |
| 104 | + // This is an enumerating expansion statement. |
| 105 | + if (auto *ILE = dyn_cast<CXXExpansionInitListExpr>(ExpansionInitializer)) { |
| 106 | + ExprResult Initializer = |
| 107 | + BuildCXXExpansionInitListSelectExpr(ILE, BuildIndexDRE(*this, ESD)); |
| 108 | + if (FinaliseExpansionVar(*this, ExpansionVar, Initializer)) |
| 109 | + return StmtError(); |
| 110 | + |
| 111 | + // Note that lifetime extension only applies to destructuring expansion |
| 112 | + // statements, so we just ignore 'LifetimeExtendedTemps' entirely for other |
| 113 | + // types of expansion statements (this is CWG 3043). |
| 114 | + return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc, |
| 115 | + ColonLoc, RParenLoc); |
| 116 | + } |
| 117 | + |
72 | 118 | Diag(ESD->getLocation(), diag::err_expansion_statements_todo); |
73 | 119 | return StmtError(); |
74 | 120 | } |
75 | 121 |
|
| 122 | +StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern( |
| 123 | + Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc, |
| 124 | + SourceLocation ColonLoc, SourceLocation RParenLoc) { |
| 125 | + return new (Context) CXXEnumeratingExpansionStmtPattern( |
| 126 | + cast<CXXExpansionStmtDecl>(ESD), Init, cast<DeclStmt>(ExpansionVar), |
| 127 | + LParenLoc, ColonLoc, RParenLoc); |
| 128 | +} |
| 129 | + |
76 | 130 | StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { |
77 | 131 | if (!Exp || !Body) |
78 | 132 | return StmtError(); |
79 | 133 |
|
| 134 | + auto *Expansion = cast<CXXExpansionStmtPattern>(Exp); |
| 135 | + assert(!Expansion->getDecl()->getInstantiations() && |
| 136 | + "should not rebuild expansion statement after instantiation"); |
| 137 | + |
| 138 | + Expansion->setBody(Body); |
| 139 | + if (Expansion->hasDependentSize()) |
| 140 | + return Expansion; |
| 141 | + |
| 142 | + // This can fail if this is an iterating expansion statement. |
| 143 | + std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion); |
| 144 | + if (!NumInstantiations) |
| 145 | + return StmtError(); |
| 146 | + |
| 147 | + // Collect shared statements. |
| 148 | + SmallVector<Stmt *, 1> Shared; |
| 149 | + if (Expansion->getInit()) |
| 150 | + Shared.push_back(Expansion->getInit()); |
| 151 | + |
| 152 | + assert(isa<CXXEnumeratingExpansionStmtPattern>(Expansion) && "TODO"); |
| 153 | + |
| 154 | + // Return an empty statement if the range is empty. |
| 155 | + if (*NumInstantiations == 0) { |
| 156 | + Expansion->getDecl()->setInstantiations( |
| 157 | + CXXExpansionStmtInstantiation::Create( |
| 158 | + Context, Expansion->getBeginLoc(), Expansion->getEndLoc(), |
| 159 | + /*Instantiations=*/{}, Shared, |
| 160 | + isa<CXXDestructuringExpansionStmtPattern>(Expansion))); |
| 161 | + return Expansion; |
| 162 | + } |
| 163 | + |
| 164 | + // Create a compound statement binding the expansion variable and body. |
| 165 | + Stmt *VarAndBody[] = {Expansion->getExpansionVarStmt(), Body}; |
| 166 | + Stmt *CombinedBody = |
| 167 | + CompoundStmt::Create(Context, VarAndBody, FPOptionsOverride(), |
| 168 | + Body->getBeginLoc(), Body->getEndLoc()); |
| 169 | + |
| 170 | + // Expand the body for each instantiation. |
| 171 | + SmallVector<Stmt *, 4> Instantiations; |
| 172 | + CXXExpansionStmtDecl *ESD = Expansion->getDecl(); |
| 173 | + for (uint64_t I = 0; I < *NumInstantiations; ++I) { |
| 174 | + // Now that we're expanding this, exit the context of the expansion stmt |
| 175 | + // so that we no longer treat this as dependent. |
| 176 | + ContextRAII CtxGuard(*this, CurContext->getParent(), |
| 177 | + /*NewThis=*/false); |
| 178 | + |
| 179 | + TemplateArgument Arg{Context, llvm::APSInt::get(I), |
| 180 | + Context.getPointerDiffType()}; |
| 181 | + MultiLevelTemplateArgumentList MTArgList(ESD, Arg, true); |
| 182 | + MTArgList.addOuterRetainedLevels( |
| 183 | + Expansion->getDecl()->getIndexTemplateParm()->getDepth()); |
| 184 | + |
| 185 | + LocalInstantiationScope LIScope(*this, /*CombineWithOuterScope=*/true); |
| 186 | + NonSFINAEContext _(*this); |
| 187 | + InstantiatingTemplate Inst(*this, Body->getBeginLoc(), Expansion, Arg, |
| 188 | + Body->getSourceRange()); |
| 189 | + |
| 190 | + StmtResult Instantiation = SubstStmt(CombinedBody, MTArgList); |
| 191 | + if (Instantiation.isInvalid()) |
| 192 | + return StmtError(); |
| 193 | + Instantiations.push_back(Instantiation.get()); |
| 194 | + } |
| 195 | + |
| 196 | + auto *InstantiationsStmt = CXXExpansionStmtInstantiation::Create( |
| 197 | + Context, Expansion->getBeginLoc(), Expansion->getEndLoc(), Instantiations, |
| 198 | + Shared, isa<CXXDestructuringExpansionStmtPattern>(Expansion)); |
| 199 | + |
| 200 | + Expansion->getDecl()->setInstantiations(InstantiationsStmt); |
| 201 | + return Expansion; |
| 202 | +} |
| 203 | + |
| 204 | +ExprResult |
| 205 | +Sema::BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range, |
| 206 | + Expr *Idx) { |
| 207 | + if (Range->containsPackExpansion() || Idx->isValueDependent()) |
| 208 | + return new (Context) CXXExpansionInitListSelectExpr(Context, Range, Idx); |
| 209 | + |
| 210 | + // The index is a DRE to a template parameter; we should never |
| 211 | + // fail to evaluate it. |
| 212 | + Expr::EvalResult ER; |
| 213 | + if (!Idx->EvaluateAsInt(ER, Context)) |
| 214 | + llvm_unreachable("Failed to evaluate expansion index"); |
| 215 | + |
| 216 | + uint64_t I = ER.Val.getInt().getZExtValue(); |
| 217 | + return Range->getExprs()[I]; |
| 218 | +} |
| 219 | + |
| 220 | +std::optional<uint64_t> |
| 221 | +Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { |
| 222 | + assert(!Expansion->hasDependentSize()); |
| 223 | + |
| 224 | + if (isa<CXXEnumeratingExpansionStmtPattern>(Expansion)) |
| 225 | + return cast<CXXExpansionInitListSelectExpr>( |
| 226 | + Expansion->getExpansionVariable()->getInit()) |
| 227 | + ->getRangeExpr() |
| 228 | + ->getExprs() |
| 229 | + .size(); |
| 230 | + |
80 | 231 | llvm_unreachable("TODO"); |
81 | 232 | } |
0 commit comments