Skip to content

Commit

Permalink
PR45083: Mark statement expressions as being dependent if they appear in
Browse files Browse the repository at this point in the history
a dependent context.

This matches the GCC behavior.

We track the enclosing template depth when determining whether a
statement expression is within a dependent context; there doesn't appear
to be any other reliable way to determine this.

We previously assumed they were neither value- nor
instantiation-dependent under any circumstances, which would lead to
crashes and other misbehavior.
  • Loading branch information
zygoloid committed Mar 10, 2020
1 parent 200b206 commit 5c845c1
Show file tree
Hide file tree
Showing 14 changed files with 231 additions and 30 deletions.
22 changes: 14 additions & 8 deletions clang/include/clang/AST/Expr.h
Expand Up @@ -3959,14 +3959,18 @@ class StmtExpr : public Expr {
Stmt *SubStmt;
SourceLocation LParenLoc, RParenLoc;
public:
// FIXME: Does type-dependence need to be computed differently?
// FIXME: Do we need to compute instantiation instantiation-dependence for
// statements? (ugh!)
StmtExpr(CompoundStmt *substmt, QualType T,
SourceLocation lp, SourceLocation rp) :
Expr(StmtExprClass, T, VK_RValue, OK_Ordinary,
T->isDependentType(), false, false, false),
SubStmt(substmt), LParenLoc(lp), RParenLoc(rp) { }
StmtExpr(CompoundStmt *SubStmt, QualType T, SourceLocation LParenLoc,
SourceLocation RParenLoc, unsigned TemplateDepth)
: // We treat a statement-expression in a dependent context as
// always being value- and instantiation-dependent. This matches the
// behavior of lambda-expressions and GCC.
Expr(StmtExprClass, T, VK_RValue, OK_Ordinary, T->isDependentType(),
TemplateDepth != 0, TemplateDepth != 0, false),
SubStmt(SubStmt), LParenLoc(LParenLoc), RParenLoc(RParenLoc) {
// FIXME: A templated statement expression should have an associated
// DeclContext so that nested declarations always have a dependent context.
StmtExprBits.TemplateDepth = TemplateDepth;
}

/// Build an empty statement expression.
explicit StmtExpr(EmptyShell Empty) : Expr(StmtExprClass, Empty) { }
Expand All @@ -3983,6 +3987,8 @@ class StmtExpr : public Expr {
SourceLocation getRParenLoc() const { return RParenLoc; }
void setRParenLoc(SourceLocation L) { RParenLoc = L; }

unsigned getTemplateDepth() const { return StmtExprBits.TemplateDepth; }

static bool classof(const Stmt *T) {
return T->getStmtClass() == StmtExprClass;
}
Expand Down
15 changes: 15 additions & 0 deletions clang/include/clang/AST/Stmt.h
Expand Up @@ -589,6 +589,18 @@ class alignas(void *) Stmt {
unsigned Kind : 2;
};

class StmtExprBitfields {
friend class ASTStmtReader;
friend class StmtExpr;

unsigned : NumExprBits;

/// The number of levels of template parameters enclosing this statement
/// expression. Used to determine if a statement expression remains
/// dependent after instantiation.
unsigned TemplateDepth;
};

//===--- C++ Expression bitfields classes ---===//

class CXXOperatorCallExprBitfields {
Expand Down Expand Up @@ -997,6 +1009,9 @@ class alignas(void *) Stmt {
PseudoObjectExprBitfields PseudoObjectExprBits;
SourceLocExprBitfields SourceLocExprBits;

// GNU Extensions.
StmtExprBitfields StmtExprBits;

// C++ Expressions
CXXOperatorCallExprBitfields CXXOperatorCallExprBits;
CXXRewrittenBinaryOperatorBitfields CXXRewrittenBinaryOperatorBits;
Expand Down
43 changes: 40 additions & 3 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -630,13 +630,32 @@ class Sema final {
/// function, block, and method scopes that are currently active.
SmallVector<sema::FunctionScopeInfo *, 4> FunctionScopes;

/// The index of the first FunctionScope that corresponds to the current
/// context.
unsigned FunctionScopesStart = 0;

ArrayRef<sema::FunctionScopeInfo*> getFunctionScopes() const {
return llvm::makeArrayRef(FunctionScopes.begin() + FunctionScopesStart,
FunctionScopes.end());
}

/// Stack containing information needed when in C++2a an 'auto' is encountered
/// in a function declaration parameter type specifier in order to invent a
/// corresponding template parameter in the enclosing abbreviated function
/// template. This information is also present in LambdaScopeInfo, stored in
/// the FunctionScopes stack.
SmallVector<InventedTemplateParameterInfo, 4> InventedParameterInfos;

/// The index of the first InventedParameterInfo that refers to the current
/// context.
unsigned InventedParameterInfosStart = 0;

ArrayRef<InventedTemplateParameterInfo> getInventedParameterInfos() const {
return llvm::makeArrayRef(InventedParameterInfos.begin() +
InventedParameterInfosStart,
InventedParameterInfos.end());
}

typedef LazyVector<TypedefNameDecl *, ExternalSemaSource,
&ExternalSemaSource::ReadExtVectorDecls, 2, 2>
ExtVectorDeclsType;
Expand Down Expand Up @@ -810,24 +829,33 @@ class Sema final {
DeclContext *SavedContext;
ProcessingContextState SavedContextState;
QualType SavedCXXThisTypeOverride;
unsigned SavedFunctionScopesStart;
unsigned SavedInventedParameterInfosStart;

public:
ContextRAII(Sema &S, DeclContext *ContextToPush, bool NewThisContext = true)
: S(S), SavedContext(S.CurContext),
SavedContextState(S.DelayedDiagnostics.pushUndelayed()),
SavedCXXThisTypeOverride(S.CXXThisTypeOverride)
SavedCXXThisTypeOverride(S.CXXThisTypeOverride),
SavedFunctionScopesStart(S.FunctionScopesStart),
SavedInventedParameterInfosStart(S.InventedParameterInfosStart)
{
assert(ContextToPush && "pushing null context");
S.CurContext = ContextToPush;
if (NewThisContext)
S.CXXThisTypeOverride = QualType();
// Any saved FunctionScopes do not refer to this context.
S.FunctionScopesStart = S.FunctionScopes.size();
S.InventedParameterInfosStart = S.InventedParameterInfos.size();
}

void pop() {
if (!SavedContext) return;
S.CurContext = SavedContext;
S.DelayedDiagnostics.popUndelayed(SavedContextState);
S.CXXThisTypeOverride = SavedCXXThisTypeOverride;
S.FunctionScopesStart = SavedFunctionScopesStart;
S.InventedParameterInfosStart = SavedInventedParameterInfosStart;
SavedContext = nullptr;
}

Expand Down Expand Up @@ -4963,8 +4991,10 @@ class Sema final {
LabelDecl *TheDecl);

void ActOnStartStmtExpr();
ExprResult ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt,
SourceLocation RPLoc); // "({..})"
ExprResult ActOnStmtExpr(Scope *S, SourceLocation LPLoc, Stmt *SubStmt,
SourceLocation RPLoc);
ExprResult BuildStmtExpr(SourceLocation LPLoc, Stmt *SubStmt,
SourceLocation RPLoc, unsigned TemplateDepth);
// Handle the final expression in a statement expression.
ExprResult ActOnStmtExprResult(ExprResult E);
void ActOnStmtExprError();
Expand Down Expand Up @@ -12020,6 +12050,13 @@ class Sema final {
return DC;
}

/// Determine the number of levels of enclosing template parameters. This is
/// only usable while parsing. Note that this does not include dependent
/// contexts in which no template parameters have yet been declared, such as
/// in a terse function template or generic lambda before the first 'auto' is
/// encountered.
unsigned getTemplateDepth(Scope *S) const;

/// To be used for checking whether the arguments being passed to
/// function exceeds the number of parameters expected for it.
static bool TooManyArguments(size_t NumParams, size_t NumArgs,
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Sema/Template.h
Expand Up @@ -95,6 +95,16 @@ class VarDecl;
return TemplateArgumentLists.size();
}

/// Determine how many of the \p OldDepth outermost template parameter
/// lists would be removed by substituting these arguments.
unsigned getNewDepth(unsigned OldDepth) const {
if (OldDepth < NumRetainedOuterLevels)
return OldDepth;
if (OldDepth < getNumLevels())
return NumRetainedOuterLevels;
return OldDepth - TemplateArgumentLists.size();
}

/// Retrieve the template argument at a given depth and index.
const TemplateArgument &operator()(unsigned Depth, unsigned Index) const {
assert(NumRetainedOuterLevels <= Depth && Depth < getNumLevels());
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/ASTImporter.cpp
Expand Up @@ -6635,8 +6635,9 @@ ExpectedStmt ASTNodeImporter::VisitStmtExpr(StmtExpr *E) {
if (Err)
return std::move(Err);

return new (Importer.getToContext()) StmtExpr(
ToSubStmt, ToType, ToLParenLoc, ToRParenLoc);
return new (Importer.getToContext())
StmtExpr(ToSubStmt, ToType, ToLParenLoc, ToRParenLoc,
E->getTemplateDepth());
}

ExpectedStmt ASTNodeImporter::VisitUnaryOperator(UnaryOperator *E) {
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Parse/ParseExpr.cpp
Expand Up @@ -2655,7 +2655,8 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,

// If the substmt parsed correctly, build the AST node.
if (!Stmt.isInvalid()) {
Result = Actions.ActOnStmtExpr(OpenLoc, Stmt.get(), Tok.getLocation());
Result = Actions.ActOnStmtExpr(getCurScope(), OpenLoc, Stmt.get(),
Tok.getLocation());
} else {
Actions.ActOnStmtExprError();
}
Expand Down
13 changes: 9 additions & 4 deletions clang/lib/Sema/SemaExpr.cpp
Expand Up @@ -13921,9 +13921,13 @@ void Sema::ActOnStmtExprError() {
PopExpressionEvaluationContext();
}

ExprResult
Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt,
SourceLocation RPLoc) { // "({..})"
ExprResult Sema::ActOnStmtExpr(Scope *S, SourceLocation LPLoc, Stmt *SubStmt,
SourceLocation RPLoc) {
return BuildStmtExpr(LPLoc, SubStmt, RPLoc, getTemplateDepth(S));
}

ExprResult Sema::BuildStmtExpr(SourceLocation LPLoc, Stmt *SubStmt,
SourceLocation RPLoc, unsigned TemplateDepth) {
assert(SubStmt && isa<CompoundStmt>(SubStmt) && "Invalid action invocation!");
CompoundStmt *Compound = cast<CompoundStmt>(SubStmt);

Expand Down Expand Up @@ -13954,7 +13958,8 @@ Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt,

// FIXME: Check that expression type is complete/non-abstract; statement
// expressions are not lvalues.
Expr *ResStmtExpr = new (Context) StmtExpr(Compound, Ty, LPLoc, RPLoc);
Expr *ResStmtExpr =
new (Context) StmtExpr(Compound, Ty, LPLoc, RPLoc, TemplateDepth);
if (StmtExprMayBindToTemp)
return MaybeBindToTemporary(ResStmtExpr);
return ResStmtExpr;
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Sema/SemaExprCXX.cpp
Expand Up @@ -6946,8 +6946,9 @@ Stmt *Sema::MaybeCreateStmtWithCleanups(Stmt *SubStmt) {
// a new AsmStmtWithTemporaries.
CompoundStmt *CompStmt = CompoundStmt::Create(
Context, SubStmt, SourceLocation(), SourceLocation());
Expr *E = new (Context) StmtExpr(CompStmt, Context.VoidTy, SourceLocation(),
SourceLocation());
Expr *E = new (Context)
StmtExpr(CompStmt, Context.VoidTy, SourceLocation(), SourceLocation(),
/*FIXME TemplateDepth=*/0);
return MaybeCreateExprWithCleanups(E);
}

Expand Down
42 changes: 42 additions & 0 deletions clang/lib/Sema/SemaTemplate.cpp
Expand Up @@ -46,6 +46,48 @@ clang::getTemplateParamsRange(TemplateParameterList const * const *Ps,
return SourceRange(Ps[0]->getTemplateLoc(), Ps[N-1]->getRAngleLoc());
}

unsigned Sema::getTemplateDepth(Scope *S) const {
unsigned Depth = 0;

// Each template parameter scope represents one level of template parameter
// depth.
for (Scope *TempParamScope = S->getTemplateParamParent();
TempParamScope && !Depth;
TempParamScope = TempParamScope->getParent()->getTemplateParamParent()) {
++Depth;
}

// Note that there are template parameters with the given depth.
auto ParamsAtDepth = [&](unsigned D) { Depth = std::max(Depth, D + 1); };

// Look for parameters of an enclosing generic lambda. We don't create a
// template parameter scope for these.
for (FunctionScopeInfo *FSI : getFunctionScopes()) {
if (auto *LSI = dyn_cast<LambdaScopeInfo>(FSI)) {
if (!LSI->TemplateParams.empty()) {
ParamsAtDepth(LSI->AutoTemplateParameterDepth);
break;
}
if (LSI->GLTemplateParameterList) {
ParamsAtDepth(LSI->GLTemplateParameterList->getDepth());
break;
}
}
}

// Look for parameters of an enclosing terse function template. We don't
// create a template parameter scope for these either.
for (const InventedTemplateParameterInfo &Info :
getInventedParameterInfos()) {
if (!Info.TemplateParams.empty()) {
ParamsAtDepth(Info.AutoTemplateParameterDepth);
break;
}
}

return Depth;
}

/// \brief Determine whether the declaration found is acceptable as the name
/// of a template and, if so, return that template declaration. Otherwise,
/// returns null.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Expand Up @@ -922,6 +922,10 @@ namespace {
this->Entity = Entity;
}

unsigned TransformTemplateDepth(unsigned Depth) {
return TemplateArgs.getNewDepth(Depth);
}

bool TryExpandParameterPacks(SourceLocation EllipsisLoc,
SourceRange PatternRange,
ArrayRef<UnexpandedParameterPack> Unexpanded,
Expand Down
28 changes: 20 additions & 8 deletions clang/lib/Sema/TreeTransform.h
Expand Up @@ -212,6 +212,14 @@ class TreeTransform {
return T.isNull();
}

/// Transform a template parameter depth level.
///
/// During a transformation that transforms template parameters, this maps
/// an old template parameter depth to a new depth.
unsigned TransformTemplateDepth(unsigned Depth) {
return Depth;
}

/// Determine whether the given call argument should be dropped, e.g.,
/// because it is a default argument.
///
Expand Down Expand Up @@ -2549,10 +2557,10 @@ class TreeTransform {
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
ExprResult RebuildStmtExpr(SourceLocation LParenLoc,
Stmt *SubStmt,
SourceLocation RParenLoc) {
return getSema().ActOnStmtExpr(LParenLoc, SubStmt, RParenLoc);
ExprResult RebuildStmtExpr(SourceLocation LParenLoc, Stmt *SubStmt,
SourceLocation RParenLoc, unsigned TemplateDepth) {
return getSema().BuildStmtExpr(LParenLoc, SubStmt, RParenLoc,
TemplateDepth);
}

/// Build a new __builtin_choose_expr expression.
Expand Down Expand Up @@ -10433,16 +10441,18 @@ TreeTransform<Derived>::TransformStmtExpr(StmtExpr *E) {
return ExprError();
}

if (!getDerived().AlwaysRebuild() &&
unsigned OldDepth = E->getTemplateDepth();
unsigned NewDepth = getDerived().TransformTemplateDepth(OldDepth);

if (!getDerived().AlwaysRebuild() && OldDepth == NewDepth &&
SubStmt.get() == E->getSubStmt()) {
// Calling this an 'error' is unintuitive, but it does the right thing.
SemaRef.ActOnStmtExprError();
return SemaRef.MaybeBindToTemporary(E);
}

return getDerived().RebuildStmtExpr(E->getLParenLoc(),
SubStmt.get(),
E->getRParenLoc());
return getDerived().RebuildStmtExpr(E->getLParenLoc(), SubStmt.get(),
E->getRParenLoc(), NewDepth);
}

template<typename Derived>
Expand Down Expand Up @@ -11888,6 +11898,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
NewTrailingRequiresClause = getDerived().TransformExpr(TRC);

// Create the local class that will describe the lambda.
// FIXME: KnownDependent below is wrong when substituting inside a templated
// context that isn't a DeclContext (such as a variable template).
CXXRecordDecl *OldClass = E->getLambdaClass();
CXXRecordDecl *Class
= getSema().createLambdaClosureType(E->getIntroducerRange(),
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTReaderStmt.cpp
Expand Up @@ -1205,6 +1205,7 @@ void ASTStmtReader::VisitStmtExpr(StmtExpr *E) {
E->setLParenLoc(readSourceLocation());
E->setRParenLoc(readSourceLocation());
E->setSubStmt(cast_or_null<CompoundStmt>(Record.readSubStmt()));
E->StmtExprBits.TemplateDepth = Record.readInt();
}

void ASTStmtReader::VisitChooseExpr(ChooseExpr *E) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTWriterStmt.cpp
Expand Up @@ -1069,6 +1069,7 @@ void ASTStmtWriter::VisitStmtExpr(StmtExpr *E) {
Record.AddStmt(E->getSubStmt());
Record.AddSourceLocation(E->getLParenLoc());
Record.AddSourceLocation(E->getRParenLoc());
Record.push_back(E->getTemplateDepth());
Code = serialization::EXPR_STMT;
}

Expand Down

0 comments on commit 5c845c1

Please sign in to comment.