xref: /openbsd-src/gnu/llvm/clang/lib/AST/Interp/ByteCodeStmtGen.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- ByteCodeStmtGen.cpp - Code generator for expressions ---*- C++ -*-===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick 
9e5dd7070Spatrick #include "ByteCodeStmtGen.h"
10e5dd7070Spatrick #include "ByteCodeEmitter.h"
11e5dd7070Spatrick #include "ByteCodeGenError.h"
12e5dd7070Spatrick #include "Context.h"
13e5dd7070Spatrick #include "Function.h"
14e5dd7070Spatrick #include "PrimType.h"
15e5dd7070Spatrick #include "Program.h"
16e5dd7070Spatrick #include "State.h"
17e5dd7070Spatrick #include "clang/Basic/LLVM.h"
18e5dd7070Spatrick 
19e5dd7070Spatrick using namespace clang;
20e5dd7070Spatrick using namespace clang::interp;
21e5dd7070Spatrick 
22e5dd7070Spatrick namespace clang {
23e5dd7070Spatrick namespace interp {
24e5dd7070Spatrick 
25e5dd7070Spatrick /// Scope managing label targets.
26e5dd7070Spatrick template <class Emitter> class LabelScope {
27e5dd7070Spatrick public:
~LabelScope()28e5dd7070Spatrick   virtual ~LabelScope() {  }
29e5dd7070Spatrick 
30e5dd7070Spatrick protected:
LabelScope(ByteCodeStmtGen<Emitter> * Ctx)31e5dd7070Spatrick   LabelScope(ByteCodeStmtGen<Emitter> *Ctx) : Ctx(Ctx) {}
32e5dd7070Spatrick   /// ByteCodeStmtGen instance.
33e5dd7070Spatrick   ByteCodeStmtGen<Emitter> *Ctx;
34e5dd7070Spatrick };
35e5dd7070Spatrick 
36e5dd7070Spatrick /// Sets the context for break/continue statements.
37e5dd7070Spatrick template <class Emitter> class LoopScope final : public LabelScope<Emitter> {
38e5dd7070Spatrick public:
39e5dd7070Spatrick   using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy;
40e5dd7070Spatrick   using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy;
41e5dd7070Spatrick 
LoopScope(ByteCodeStmtGen<Emitter> * Ctx,LabelTy BreakLabel,LabelTy ContinueLabel)42e5dd7070Spatrick   LoopScope(ByteCodeStmtGen<Emitter> *Ctx, LabelTy BreakLabel,
43e5dd7070Spatrick             LabelTy ContinueLabel)
44e5dd7070Spatrick       : LabelScope<Emitter>(Ctx), OldBreakLabel(Ctx->BreakLabel),
45e5dd7070Spatrick         OldContinueLabel(Ctx->ContinueLabel) {
46e5dd7070Spatrick     this->Ctx->BreakLabel = BreakLabel;
47e5dd7070Spatrick     this->Ctx->ContinueLabel = ContinueLabel;
48e5dd7070Spatrick   }
49e5dd7070Spatrick 
~LoopScope()50e5dd7070Spatrick   ~LoopScope() {
51e5dd7070Spatrick     this->Ctx->BreakLabel = OldBreakLabel;
52e5dd7070Spatrick     this->Ctx->ContinueLabel = OldContinueLabel;
53e5dd7070Spatrick   }
54e5dd7070Spatrick 
55e5dd7070Spatrick private:
56e5dd7070Spatrick   OptLabelTy OldBreakLabel;
57e5dd7070Spatrick   OptLabelTy OldContinueLabel;
58e5dd7070Spatrick };
59e5dd7070Spatrick 
60e5dd7070Spatrick // Sets the context for a switch scope, mapping labels.
61e5dd7070Spatrick template <class Emitter> class SwitchScope final : public LabelScope<Emitter> {
62e5dd7070Spatrick public:
63e5dd7070Spatrick   using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy;
64e5dd7070Spatrick   using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy;
65e5dd7070Spatrick   using CaseMap = typename ByteCodeStmtGen<Emitter>::CaseMap;
66e5dd7070Spatrick 
SwitchScope(ByteCodeStmtGen<Emitter> * Ctx,CaseMap && CaseLabels,LabelTy BreakLabel,OptLabelTy DefaultLabel)67e5dd7070Spatrick   SwitchScope(ByteCodeStmtGen<Emitter> *Ctx, CaseMap &&CaseLabels,
68e5dd7070Spatrick               LabelTy BreakLabel, OptLabelTy DefaultLabel)
69e5dd7070Spatrick       : LabelScope<Emitter>(Ctx), OldBreakLabel(Ctx->BreakLabel),
70e5dd7070Spatrick         OldDefaultLabel(this->Ctx->DefaultLabel),
71e5dd7070Spatrick         OldCaseLabels(std::move(this->Ctx->CaseLabels)) {
72e5dd7070Spatrick     this->Ctx->BreakLabel = BreakLabel;
73e5dd7070Spatrick     this->Ctx->DefaultLabel = DefaultLabel;
74e5dd7070Spatrick     this->Ctx->CaseLabels = std::move(CaseLabels);
75e5dd7070Spatrick   }
76e5dd7070Spatrick 
~SwitchScope()77e5dd7070Spatrick   ~SwitchScope() {
78e5dd7070Spatrick     this->Ctx->BreakLabel = OldBreakLabel;
79e5dd7070Spatrick     this->Ctx->DefaultLabel = OldDefaultLabel;
80e5dd7070Spatrick     this->Ctx->CaseLabels = std::move(OldCaseLabels);
81e5dd7070Spatrick   }
82e5dd7070Spatrick 
83e5dd7070Spatrick private:
84e5dd7070Spatrick   OptLabelTy OldBreakLabel;
85e5dd7070Spatrick   OptLabelTy OldDefaultLabel;
86e5dd7070Spatrick   CaseMap OldCaseLabels;
87e5dd7070Spatrick };
88e5dd7070Spatrick 
89e5dd7070Spatrick } // namespace interp
90e5dd7070Spatrick } // namespace clang
91e5dd7070Spatrick 
92e5dd7070Spatrick template <class Emitter>
visitFunc(const FunctionDecl * F)93e5dd7070Spatrick bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
94e5dd7070Spatrick   // Classify the return type.
95e5dd7070Spatrick   ReturnType = this->classify(F->getReturnType());
96e5dd7070Spatrick 
97*12c85518Srobert   // Constructor. Set up field initializers.
98*12c85518Srobert   if (const auto Ctor = dyn_cast<CXXConstructorDecl>(F)) {
99*12c85518Srobert     const RecordDecl *RD = Ctor->getParent();
100*12c85518Srobert     const Record *R = this->getRecord(RD);
101*12c85518Srobert     if (!R)
102*12c85518Srobert       return false;
103e5dd7070Spatrick 
104*12c85518Srobert     for (const auto *Init : Ctor->inits()) {
105*12c85518Srobert       const Expr *InitExpr = Init->getInit();
106*12c85518Srobert       if (const FieldDecl *Member = Init->getMember()) {
107*12c85518Srobert         const Record::Field *F = R->getField(Member);
108*12c85518Srobert 
109*12c85518Srobert         if (std::optional<PrimType> T = this->classify(InitExpr)) {
110*12c85518Srobert           if (!this->emitThis(InitExpr))
111*12c85518Srobert             return false;
112*12c85518Srobert 
113*12c85518Srobert           if (!this->visit(InitExpr))
114*12c85518Srobert             return false;
115*12c85518Srobert 
116*12c85518Srobert           if (!this->emitInitField(*T, F->Offset, InitExpr))
117*12c85518Srobert             return false;
118*12c85518Srobert 
119*12c85518Srobert           if (!this->emitPopPtr(InitExpr))
120*12c85518Srobert             return false;
121*12c85518Srobert         } else {
122*12c85518Srobert           // Non-primitive case. Get a pointer to the field-to-initialize
123*12c85518Srobert           // on the stack and call visitInitialzer() for it.
124*12c85518Srobert           if (!this->emitThis(InitExpr))
125*12c85518Srobert             return false;
126*12c85518Srobert 
127*12c85518Srobert           if (!this->emitGetPtrField(F->Offset, InitExpr))
128*12c85518Srobert             return false;
129*12c85518Srobert 
130*12c85518Srobert           if (!this->visitInitializer(InitExpr))
131*12c85518Srobert             return false;
132*12c85518Srobert 
133*12c85518Srobert           if (!this->emitPopPtr(InitExpr))
134*12c85518Srobert             return false;
135*12c85518Srobert         }
136*12c85518Srobert       } else if (const Type *Base = Init->getBaseClass()) {
137*12c85518Srobert         // Base class initializer.
138*12c85518Srobert         // Get This Base and call initializer on it.
139*12c85518Srobert         auto *BaseDecl = Base->getAsCXXRecordDecl();
140*12c85518Srobert         assert(BaseDecl);
141*12c85518Srobert         const Record::Base *B = R->getBase(BaseDecl);
142*12c85518Srobert         assert(B);
143*12c85518Srobert         if (!this->emitGetPtrThisBase(B->Offset, InitExpr))
144*12c85518Srobert           return false;
145*12c85518Srobert         if (!this->visitInitializer(InitExpr))
146*12c85518Srobert           return false;
147*12c85518Srobert         if (!this->emitPopPtr(InitExpr))
148*12c85518Srobert           return false;
149*12c85518Srobert       }
150*12c85518Srobert     }
151*12c85518Srobert   }
152*12c85518Srobert 
153*12c85518Srobert   if (const auto *Body = F->getBody())
154e5dd7070Spatrick     if (!visitStmt(Body))
155e5dd7070Spatrick       return false;
156e5dd7070Spatrick 
157e5dd7070Spatrick   // Emit a guard return to protect against a code path missing one.
158e5dd7070Spatrick   if (F->getReturnType()->isVoidType())
159e5dd7070Spatrick     return this->emitRetVoid(SourceInfo{});
160e5dd7070Spatrick   else
161e5dd7070Spatrick     return this->emitNoRet(SourceInfo{});
162e5dd7070Spatrick }
163e5dd7070Spatrick 
164e5dd7070Spatrick template <class Emitter>
visitStmt(const Stmt * S)165e5dd7070Spatrick bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) {
166e5dd7070Spatrick   switch (S->getStmtClass()) {
167e5dd7070Spatrick   case Stmt::CompoundStmtClass:
168e5dd7070Spatrick     return visitCompoundStmt(cast<CompoundStmt>(S));
169e5dd7070Spatrick   case Stmt::DeclStmtClass:
170e5dd7070Spatrick     return visitDeclStmt(cast<DeclStmt>(S));
171e5dd7070Spatrick   case Stmt::ReturnStmtClass:
172e5dd7070Spatrick     return visitReturnStmt(cast<ReturnStmt>(S));
173e5dd7070Spatrick   case Stmt::IfStmtClass:
174e5dd7070Spatrick     return visitIfStmt(cast<IfStmt>(S));
175*12c85518Srobert   case Stmt::WhileStmtClass:
176*12c85518Srobert     return visitWhileStmt(cast<WhileStmt>(S));
177*12c85518Srobert   case Stmt::DoStmtClass:
178*12c85518Srobert     return visitDoStmt(cast<DoStmt>(S));
179*12c85518Srobert   case Stmt::ForStmtClass:
180*12c85518Srobert     return visitForStmt(cast<ForStmt>(S));
181*12c85518Srobert   case Stmt::BreakStmtClass:
182*12c85518Srobert     return visitBreakStmt(cast<BreakStmt>(S));
183*12c85518Srobert   case Stmt::ContinueStmtClass:
184*12c85518Srobert     return visitContinueStmt(cast<ContinueStmt>(S));
185e5dd7070Spatrick   case Stmt::NullStmtClass:
186e5dd7070Spatrick     return true;
187e5dd7070Spatrick   default: {
188e5dd7070Spatrick     if (auto *Exp = dyn_cast<Expr>(S))
189e5dd7070Spatrick       return this->discard(Exp);
190e5dd7070Spatrick     return this->bail(S);
191e5dd7070Spatrick   }
192e5dd7070Spatrick   }
193e5dd7070Spatrick }
194e5dd7070Spatrick 
195e5dd7070Spatrick template <class Emitter>
visitCompoundStmt(const CompoundStmt * CompoundStmt)196e5dd7070Spatrick bool ByteCodeStmtGen<Emitter>::visitCompoundStmt(
197e5dd7070Spatrick     const CompoundStmt *CompoundStmt) {
198e5dd7070Spatrick   BlockScope<Emitter> Scope(this);
199e5dd7070Spatrick   for (auto *InnerStmt : CompoundStmt->body())
200e5dd7070Spatrick     if (!visitStmt(InnerStmt))
201e5dd7070Spatrick       return false;
202e5dd7070Spatrick   return true;
203e5dd7070Spatrick }
204e5dd7070Spatrick 
205e5dd7070Spatrick template <class Emitter>
visitDeclStmt(const DeclStmt * DS)206e5dd7070Spatrick bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) {
207e5dd7070Spatrick   for (auto *D : DS->decls()) {
208e5dd7070Spatrick     // Variable declarator.
209e5dd7070Spatrick     if (auto *VD = dyn_cast<VarDecl>(D)) {
210*12c85518Srobert       if (!this->visitVarDecl(VD))
211e5dd7070Spatrick         return false;
212e5dd7070Spatrick       continue;
213e5dd7070Spatrick     }
214e5dd7070Spatrick 
215e5dd7070Spatrick     // Decomposition declarator.
216e5dd7070Spatrick     if (auto *DD = dyn_cast<DecompositionDecl>(D)) {
217e5dd7070Spatrick       return this->bail(DD);
218e5dd7070Spatrick     }
219e5dd7070Spatrick   }
220e5dd7070Spatrick 
221e5dd7070Spatrick   return true;
222e5dd7070Spatrick }
223e5dd7070Spatrick 
224e5dd7070Spatrick template <class Emitter>
visitReturnStmt(const ReturnStmt * RS)225e5dd7070Spatrick bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) {
226e5dd7070Spatrick   if (const Expr *RE = RS->getRetValue()) {
227e5dd7070Spatrick     ExprScope<Emitter> RetScope(this);
228e5dd7070Spatrick     if (ReturnType) {
229e5dd7070Spatrick       // Primitive types are simply returned.
230e5dd7070Spatrick       if (!this->visit(RE))
231e5dd7070Spatrick         return false;
232e5dd7070Spatrick       this->emitCleanup();
233e5dd7070Spatrick       return this->emitRet(*ReturnType, RS);
234e5dd7070Spatrick     } else {
235e5dd7070Spatrick       // RVO - construct the value in the return location.
236*12c85518Srobert       if (!this->emitRVOPtr(RE))
237e5dd7070Spatrick         return false;
238*12c85518Srobert       if (!this->visitInitializer(RE))
239*12c85518Srobert         return false;
240*12c85518Srobert       if (!this->emitPopPtr(RE))
241*12c85518Srobert         return false;
242*12c85518Srobert 
243e5dd7070Spatrick       this->emitCleanup();
244e5dd7070Spatrick       return this->emitRetVoid(RS);
245e5dd7070Spatrick     }
246e5dd7070Spatrick   }
247*12c85518Srobert 
248*12c85518Srobert   // Void return.
249*12c85518Srobert   this->emitCleanup();
250*12c85518Srobert   return this->emitRetVoid(RS);
251e5dd7070Spatrick }
252e5dd7070Spatrick 
253e5dd7070Spatrick template <class Emitter>
visitIfStmt(const IfStmt * IS)254e5dd7070Spatrick bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
255e5dd7070Spatrick   BlockScope<Emitter> IfScope(this);
256*12c85518Srobert 
257*12c85518Srobert   if (IS->isNonNegatedConsteval())
258*12c85518Srobert     return visitStmt(IS->getThen());
259*12c85518Srobert   if (IS->isNegatedConsteval())
260*12c85518Srobert     return IS->getElse() ? visitStmt(IS->getElse()) : true;
261*12c85518Srobert 
262e5dd7070Spatrick   if (auto *CondInit = IS->getInit())
263e5dd7070Spatrick     if (!visitStmt(IS->getInit()))
264e5dd7070Spatrick       return false;
265e5dd7070Spatrick 
266e5dd7070Spatrick   if (const DeclStmt *CondDecl = IS->getConditionVariableDeclStmt())
267e5dd7070Spatrick     if (!visitDeclStmt(CondDecl))
268e5dd7070Spatrick       return false;
269e5dd7070Spatrick 
270e5dd7070Spatrick   if (!this->visitBool(IS->getCond()))
271e5dd7070Spatrick     return false;
272e5dd7070Spatrick 
273e5dd7070Spatrick   if (const Stmt *Else = IS->getElse()) {
274e5dd7070Spatrick     LabelTy LabelElse = this->getLabel();
275e5dd7070Spatrick     LabelTy LabelEnd = this->getLabel();
276e5dd7070Spatrick     if (!this->jumpFalse(LabelElse))
277e5dd7070Spatrick       return false;
278e5dd7070Spatrick     if (!visitStmt(IS->getThen()))
279e5dd7070Spatrick       return false;
280e5dd7070Spatrick     if (!this->jump(LabelEnd))
281e5dd7070Spatrick       return false;
282e5dd7070Spatrick     this->emitLabel(LabelElse);
283e5dd7070Spatrick     if (!visitStmt(Else))
284e5dd7070Spatrick       return false;
285e5dd7070Spatrick     this->emitLabel(LabelEnd);
286e5dd7070Spatrick   } else {
287e5dd7070Spatrick     LabelTy LabelEnd = this->getLabel();
288e5dd7070Spatrick     if (!this->jumpFalse(LabelEnd))
289e5dd7070Spatrick       return false;
290e5dd7070Spatrick     if (!visitStmt(IS->getThen()))
291e5dd7070Spatrick       return false;
292e5dd7070Spatrick     this->emitLabel(LabelEnd);
293e5dd7070Spatrick   }
294e5dd7070Spatrick 
295e5dd7070Spatrick   return true;
296e5dd7070Spatrick }
297e5dd7070Spatrick 
298e5dd7070Spatrick template <class Emitter>
visitWhileStmt(const WhileStmt * S)299*12c85518Srobert bool ByteCodeStmtGen<Emitter>::visitWhileStmt(const WhileStmt *S) {
300*12c85518Srobert   const Expr *Cond = S->getCond();
301*12c85518Srobert   const Stmt *Body = S->getBody();
302e5dd7070Spatrick 
303*12c85518Srobert   LabelTy CondLabel = this->getLabel(); // Label before the condition.
304*12c85518Srobert   LabelTy EndLabel = this->getLabel();  // Label after the loop.
305*12c85518Srobert   LoopScope<Emitter> LS(this, EndLabel, CondLabel);
306*12c85518Srobert 
307*12c85518Srobert   this->emitLabel(CondLabel);
308*12c85518Srobert   if (!this->visitBool(Cond))
309*12c85518Srobert     return false;
310*12c85518Srobert   if (!this->jumpFalse(EndLabel))
311*12c85518Srobert     return false;
312*12c85518Srobert 
313*12c85518Srobert   if (!this->visitStmt(Body))
314*12c85518Srobert     return false;
315*12c85518Srobert   if (!this->jump(CondLabel))
316*12c85518Srobert     return false;
317*12c85518Srobert 
318*12c85518Srobert   this->emitLabel(EndLabel);
319*12c85518Srobert 
320e5dd7070Spatrick   return true;
321e5dd7070Spatrick }
322e5dd7070Spatrick 
323*12c85518Srobert template <class Emitter>
visitDoStmt(const DoStmt * S)324*12c85518Srobert bool ByteCodeStmtGen<Emitter>::visitDoStmt(const DoStmt *S) {
325*12c85518Srobert   const Expr *Cond = S->getCond();
326*12c85518Srobert   const Stmt *Body = S->getBody();
327*12c85518Srobert 
328*12c85518Srobert   LabelTy StartLabel = this->getLabel();
329*12c85518Srobert   LabelTy EndLabel = this->getLabel();
330*12c85518Srobert   LabelTy CondLabel = this->getLabel();
331*12c85518Srobert   LoopScope<Emitter> LS(this, EndLabel, CondLabel);
332*12c85518Srobert 
333*12c85518Srobert   this->emitLabel(StartLabel);
334*12c85518Srobert   if (!this->visitStmt(Body))
335*12c85518Srobert     return false;
336*12c85518Srobert   this->emitLabel(CondLabel);
337*12c85518Srobert   if (!this->visitBool(Cond))
338*12c85518Srobert     return false;
339*12c85518Srobert   if (!this->jumpTrue(StartLabel))
340*12c85518Srobert     return false;
341*12c85518Srobert   this->emitLabel(EndLabel);
342*12c85518Srobert   return true;
343*12c85518Srobert }
344*12c85518Srobert 
345*12c85518Srobert template <class Emitter>
visitForStmt(const ForStmt * S)346*12c85518Srobert bool ByteCodeStmtGen<Emitter>::visitForStmt(const ForStmt *S) {
347*12c85518Srobert   // for (Init; Cond; Inc) { Body }
348*12c85518Srobert   const Stmt *Init = S->getInit();
349*12c85518Srobert   const Expr *Cond = S->getCond();
350*12c85518Srobert   const Expr *Inc = S->getInc();
351*12c85518Srobert   const Stmt *Body = S->getBody();
352*12c85518Srobert 
353*12c85518Srobert   LabelTy EndLabel = this->getLabel();
354*12c85518Srobert   LabelTy CondLabel = this->getLabel();
355*12c85518Srobert   LabelTy IncLabel = this->getLabel();
356*12c85518Srobert   LoopScope<Emitter> LS(this, EndLabel, IncLabel);
357*12c85518Srobert 
358*12c85518Srobert   if (Init && !this->visitStmt(Init))
359*12c85518Srobert     return false;
360*12c85518Srobert   this->emitLabel(CondLabel);
361*12c85518Srobert   if (Cond) {
362*12c85518Srobert     if (!this->visitBool(Cond))
363*12c85518Srobert       return false;
364*12c85518Srobert     if (!this->jumpFalse(EndLabel))
365e5dd7070Spatrick       return false;
366e5dd7070Spatrick   }
367*12c85518Srobert   if (Body && !this->visitStmt(Body))
368*12c85518Srobert     return false;
369*12c85518Srobert   this->emitLabel(IncLabel);
370*12c85518Srobert   if (Inc && !this->discard(Inc))
371*12c85518Srobert     return false;
372*12c85518Srobert   if (!this->jump(CondLabel))
373*12c85518Srobert     return false;
374*12c85518Srobert   this->emitLabel(EndLabel);
375*12c85518Srobert   return true;
376e5dd7070Spatrick }
377*12c85518Srobert 
378*12c85518Srobert template <class Emitter>
visitBreakStmt(const BreakStmt * S)379*12c85518Srobert bool ByteCodeStmtGen<Emitter>::visitBreakStmt(const BreakStmt *S) {
380*12c85518Srobert   if (!BreakLabel)
381*12c85518Srobert     return false;
382*12c85518Srobert 
383*12c85518Srobert   return this->jump(*BreakLabel);
384e5dd7070Spatrick }
385*12c85518Srobert 
386*12c85518Srobert template <class Emitter>
visitContinueStmt(const ContinueStmt * S)387*12c85518Srobert bool ByteCodeStmtGen<Emitter>::visitContinueStmt(const ContinueStmt *S) {
388*12c85518Srobert   if (!ContinueLabel)
389*12c85518Srobert     return false;
390*12c85518Srobert 
391*12c85518Srobert   return this->jump(*ContinueLabel);
392e5dd7070Spatrick }
393e5dd7070Spatrick 
394e5dd7070Spatrick namespace clang {
395e5dd7070Spatrick namespace interp {
396e5dd7070Spatrick 
397e5dd7070Spatrick template class ByteCodeStmtGen<ByteCodeEmitter>;
398e5dd7070Spatrick 
399e5dd7070Spatrick } // namespace interp
400e5dd7070Spatrick } // namespace clang
401