mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 22:56:05 +00:00

Implement P2036R3. Captured variables by copy (explicitely or not), are deduced correctly at the point we know whether the lambda is mutable, and ill-formed before that. Up until now, the entire lambda declaration up to the start of the body would be parsed in the parent scope, such that capture would not be available to look up. The scoping is changed to have an outer lambda scope, followed by the lambda prototype and body. The lambda scope is necessary because there may be a template scope between the start of the lambda (to which we want to attach the captured variable) and the prototype scope. We also need to introduce a declaration context to attach the captured variable to (and several parts of clang assume captures are handled from the call operator context), before we know the type of the call operator. The order of operations is as follow: * Parse the init capture in the lambda's parent scope * Introduce a lambda scope * Create the lambda class and call operator * Add the init captures to the call operator context and the lambda scope. But the variables are not capured yet (because we don't know their type). Instead, explicit captures are stored in a temporary map that conserves the order of capture (for the purpose of having a stable order in the ast dumps). * A flag is set on LambdaScopeInfo to indicate that we have not yet injected the captures. * The parameters are parsed (in the parent context, as lambda mangling recurses in the parent context, we couldn't mangle a lambda that is attached to the context of a lambda whose type is not yet known). * The lambda qualifiers are parsed, at this point We can switch (for the second time) inside the lambda context, unset the flag indicating that we have not parsed the lambda qualifiers, record the lambda is mutable and capture the explicit variables. * We can parse the rest of the lambda type, transform the lambda and call operator's types and also transform the call operator to a template function decl where necessary. At this point, both captures and parameters can be injected in the body's scope. When trying to capture an implicit variable, if we are before the qualifiers of a lambda, we need to remember that the variables are still in the parent's context (rather than in the call operator's). Reviewed By: aaron.ballman, #clang-language-wg, ChuanqiXu Differential Revision: https://reviews.llvm.org/D119136
203 lines
6.3 KiB
C++
203 lines
6.3 KiB
C++
//===- Scope.cpp - Lexical scope information --------------------*- C++ -*-===//
|
|
//
|
|
// 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 the Scope class, which is used for recording
|
|
// information about a lexical scope.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Sema/Scope.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace clang;
|
|
|
|
void Scope::setFlags(Scope *parent, unsigned flags) {
|
|
AnyParent = parent;
|
|
Flags = flags;
|
|
|
|
if (parent && !(flags & FnScope)) {
|
|
BreakParent = parent->BreakParent;
|
|
ContinueParent = parent->ContinueParent;
|
|
} else {
|
|
// Control scopes do not contain the contents of nested function scopes for
|
|
// control flow purposes.
|
|
BreakParent = ContinueParent = nullptr;
|
|
}
|
|
|
|
if (parent) {
|
|
Depth = parent->Depth + 1;
|
|
PrototypeDepth = parent->PrototypeDepth;
|
|
PrototypeIndex = 0;
|
|
FnParent = parent->FnParent;
|
|
BlockParent = parent->BlockParent;
|
|
TemplateParamParent = parent->TemplateParamParent;
|
|
MSLastManglingParent = parent->MSLastManglingParent;
|
|
MSCurManglingNumber = getMSLastManglingNumber();
|
|
if ((Flags & (FnScope | ClassScope | BlockScope | TemplateParamScope |
|
|
FunctionPrototypeScope | AtCatchScope | ObjCMethodScope)) ==
|
|
0)
|
|
Flags |= parent->getFlags() & OpenMPSimdDirectiveScope;
|
|
} else {
|
|
Depth = 0;
|
|
PrototypeDepth = 0;
|
|
PrototypeIndex = 0;
|
|
MSLastManglingParent = FnParent = BlockParent = nullptr;
|
|
TemplateParamParent = nullptr;
|
|
MSLastManglingNumber = 1;
|
|
MSCurManglingNumber = 1;
|
|
}
|
|
|
|
// If this scope is a function or contains breaks/continues, remember it.
|
|
if (flags & FnScope) FnParent = this;
|
|
// The MS mangler uses the number of scopes that can hold declarations as
|
|
// part of an external name.
|
|
if (Flags & (ClassScope | FnScope)) {
|
|
MSLastManglingNumber = getMSLastManglingNumber();
|
|
MSLastManglingParent = this;
|
|
MSCurManglingNumber = 1;
|
|
}
|
|
if (flags & BreakScope) BreakParent = this;
|
|
if (flags & ContinueScope) ContinueParent = this;
|
|
if (flags & BlockScope) BlockParent = this;
|
|
if (flags & TemplateParamScope) TemplateParamParent = this;
|
|
|
|
// If this is a prototype scope, record that. Lambdas have an extra prototype
|
|
// scope that doesn't add any depth.
|
|
if (flags & FunctionPrototypeScope && !(flags & LambdaScope))
|
|
PrototypeDepth++;
|
|
|
|
if (flags & DeclScope) {
|
|
if (flags & FunctionPrototypeScope)
|
|
; // Prototype scopes are uninteresting.
|
|
else if ((flags & ClassScope) && getParent()->isClassScope())
|
|
; // Nested class scopes aren't ambiguous.
|
|
else if ((flags & ClassScope) && getParent()->getFlags() == DeclScope)
|
|
; // Classes inside of namespaces aren't ambiguous.
|
|
else if ((flags & EnumScope))
|
|
; // Don't increment for enum scopes.
|
|
else
|
|
incrementMSManglingNumber();
|
|
}
|
|
}
|
|
|
|
void Scope::Init(Scope *parent, unsigned flags) {
|
|
setFlags(parent, flags);
|
|
|
|
DeclsInScope.clear();
|
|
UsingDirectives.clear();
|
|
Entity = nullptr;
|
|
ErrorTrap.reset();
|
|
NRVO.setPointerAndInt(nullptr, false);
|
|
}
|
|
|
|
bool Scope::containedInPrototypeScope() const {
|
|
const Scope *S = this;
|
|
while (S) {
|
|
if (S->isFunctionPrototypeScope())
|
|
return true;
|
|
S = S->getParent();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Scope::AddFlags(unsigned FlagsToSet) {
|
|
assert((FlagsToSet & ~(BreakScope | ContinueScope)) == 0 &&
|
|
"Unsupported scope flags");
|
|
if (FlagsToSet & BreakScope) {
|
|
assert((Flags & BreakScope) == 0 && "Already set");
|
|
BreakParent = this;
|
|
}
|
|
if (FlagsToSet & ContinueScope) {
|
|
assert((Flags & ContinueScope) == 0 && "Already set");
|
|
ContinueParent = this;
|
|
}
|
|
Flags |= FlagsToSet;
|
|
}
|
|
|
|
void Scope::mergeNRVOIntoParent() {
|
|
if (VarDecl *Candidate = NRVO.getPointer()) {
|
|
if (isDeclScope(Candidate))
|
|
Candidate->setNRVOVariable(true);
|
|
}
|
|
|
|
if (getEntity())
|
|
return;
|
|
|
|
if (NRVO.getInt())
|
|
getParent()->setNoNRVO();
|
|
else if (NRVO.getPointer())
|
|
getParent()->addNRVOCandidate(NRVO.getPointer());
|
|
}
|
|
|
|
LLVM_DUMP_METHOD void Scope::dump() const { dumpImpl(llvm::errs()); }
|
|
|
|
void Scope::dumpImpl(raw_ostream &OS) const {
|
|
unsigned Flags = getFlags();
|
|
bool HasFlags = Flags != 0;
|
|
|
|
if (HasFlags)
|
|
OS << "Flags: ";
|
|
|
|
std::pair<unsigned, const char *> FlagInfo[] = {
|
|
{FnScope, "FnScope"},
|
|
{BreakScope, "BreakScope"},
|
|
{ContinueScope, "ContinueScope"},
|
|
{DeclScope, "DeclScope"},
|
|
{ControlScope, "ControlScope"},
|
|
{ClassScope, "ClassScope"},
|
|
{BlockScope, "BlockScope"},
|
|
{TemplateParamScope, "TemplateParamScope"},
|
|
{FunctionPrototypeScope, "FunctionPrototypeScope"},
|
|
{FunctionDeclarationScope, "FunctionDeclarationScope"},
|
|
{AtCatchScope, "AtCatchScope"},
|
|
{ObjCMethodScope, "ObjCMethodScope"},
|
|
{SwitchScope, "SwitchScope"},
|
|
{TryScope, "TryScope"},
|
|
{FnTryCatchScope, "FnTryCatchScope"},
|
|
{OpenMPDirectiveScope, "OpenMPDirectiveScope"},
|
|
{OpenMPLoopDirectiveScope, "OpenMPLoopDirectiveScope"},
|
|
{OpenMPSimdDirectiveScope, "OpenMPSimdDirectiveScope"},
|
|
{EnumScope, "EnumScope"},
|
|
{SEHTryScope, "SEHTryScope"},
|
|
{SEHExceptScope, "SEHExceptScope"},
|
|
{SEHFilterScope, "SEHFilterScope"},
|
|
{CompoundStmtScope, "CompoundStmtScope"},
|
|
{ClassInheritanceScope, "ClassInheritanceScope"},
|
|
{CatchScope, "CatchScope"},
|
|
};
|
|
|
|
for (auto Info : FlagInfo) {
|
|
if (Flags & Info.first) {
|
|
OS << Info.second;
|
|
Flags &= ~Info.first;
|
|
if (Flags)
|
|
OS << " | ";
|
|
}
|
|
}
|
|
|
|
assert(Flags == 0 && "Unknown scope flags");
|
|
|
|
if (HasFlags)
|
|
OS << '\n';
|
|
|
|
if (const Scope *Parent = getParent())
|
|
OS << "Parent: (clang::Scope*)" << Parent << '\n';
|
|
|
|
OS << "Depth: " << Depth << '\n';
|
|
OS << "MSLastManglingNumber: " << getMSLastManglingNumber() << '\n';
|
|
OS << "MSCurManglingNumber: " << getMSCurManglingNumber() << '\n';
|
|
if (const DeclContext *DC = getEntity())
|
|
OS << "Entity : (clang::DeclContext*)" << DC << '\n';
|
|
|
|
if (NRVO.getInt())
|
|
OS << "NRVO not allowed\n";
|
|
else if (NRVO.getPointer())
|
|
OS << "NRVO candidate : (clang::VarDecl*)" << NRVO.getPointer() << '\n';
|
|
}
|