
This is a major change on how we represent nested name qualifications in the AST. * The nested name specifier itself and how it's stored is changed. The prefixes for types are handled within the type hierarchy, which makes canonicalization for them super cheap, no memory allocation required. Also translating a type into nested name specifier form becomes a no-op. An identifier is stored as a DependentNameType. The nested name specifier gains a lightweight handle class, to be used instead of passing around pointers, which is similar to what is implemented for TemplateName. There is still one free bit available, and this handle can be used within a PointerUnion and PointerIntPair, which should keep bit-packing aficionados happy. * The ElaboratedType node is removed, all type nodes in which it could previously apply to can now store the elaborated keyword and name qualifier, tail allocating when present. * TagTypes can now point to the exact declaration found when producing these, as opposed to the previous situation of there only existing one TagType per entity. This increases the amount of type sugar retained, and can have several applications, for example in tracking module ownership, and other tools which care about source file origins, such as IWYU. These TagTypes are lazily allocated, in order to limit the increase in AST size. This patch offers a great performance benefit. It greatly improves compilation time for [stdexec](https://github.com/NVIDIA/stdexec). For one datapoint, for `test_on2.cpp` in that project, which is the slowest compiling test, this patch improves `-c` compilation time by about 7.2%, with the `-fsyntax-only` improvement being at ~12%. This has great results on compile-time-tracker as well:  This patch also further enables other optimziations in the future, and will reduce the performance impact of template specialization resugaring when that lands. It has some other miscelaneous drive-by fixes. About the review: Yes the patch is huge, sorry about that. Part of the reason is that I started by the nested name specifier part, before the ElaboratedType part, but that had a huge performance downside, as ElaboratedType is a big performance hog. I didn't have the steam to go back and change the patch after the fact. There is also a lot of internal API changes, and it made sense to remove ElaboratedType in one go, versus removing it from one type at a time, as that would present much more churn to the users. Also, the nested name specifier having a different API avoids missing changes related to how prefixes work now, which could make existing code compile but not work. How to review: The important changes are all in `clang/include/clang/AST` and `clang/lib/AST`, with also important changes in `clang/lib/Sema/TreeTransform.h`. The rest and bulk of the changes are mostly consequences of the changes in API. PS: TagType::getDecl is renamed to `getOriginalDecl` in this patch, just for easier to rebasing. I plan to rename it back after this lands. Fixes #136624 Fixes https://github.com/llvm/llvm-project/issues/43179 Fixes https://github.com/llvm/llvm-project/issues/68670 Fixes https://github.com/llvm/llvm-project/issues/92757
1077 lines
41 KiB
C++
1077 lines
41 KiB
C++
//===--- SemaCXXScopeSpec.cpp - Semantic Analysis for C++ scope specifiers-===//
|
|
//
|
|
// 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 C++ semantic analysis for scope specifiers.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "TypeLocBuilder.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/DeclTemplate.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "clang/AST/NestedNameSpecifier.h"
|
|
#include "clang/Basic/PartialDiagnostic.h"
|
|
#include "clang/Sema/DeclSpec.h"
|
|
#include "clang/Sema/Lookup.h"
|
|
#include "clang/Sema/Template.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
using namespace clang;
|
|
|
|
/// Find the current instantiation that associated with the given type.
|
|
static CXXRecordDecl *getCurrentInstantiationOf(QualType T,
|
|
DeclContext *CurContext) {
|
|
if (T.isNull())
|
|
return nullptr;
|
|
|
|
const TagType *TagTy = dyn_cast<TagType>(T->getCanonicalTypeInternal());
|
|
if (!isa_and_present<RecordType, InjectedClassNameType>(TagTy))
|
|
return nullptr;
|
|
auto *RD =
|
|
cast<CXXRecordDecl>(TagTy->getOriginalDecl())->getDefinitionOrSelf();
|
|
if (isa<InjectedClassNameType>(TagTy) ||
|
|
RD->isCurrentInstantiation(CurContext))
|
|
return RD;
|
|
return nullptr;
|
|
}
|
|
|
|
DeclContext *Sema::computeDeclContext(QualType T) {
|
|
if (!T->isDependentType())
|
|
if (auto *D = T->getAsTagDecl())
|
|
return D;
|
|
return ::getCurrentInstantiationOf(T, CurContext);
|
|
}
|
|
|
|
DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
|
|
bool EnteringContext) {
|
|
if (!SS.isSet() || SS.isInvalid())
|
|
return nullptr;
|
|
|
|
NestedNameSpecifier NNS = SS.getScopeRep();
|
|
if (NNS.isDependent()) {
|
|
// If this nested-name-specifier refers to the current
|
|
// instantiation, return its DeclContext.
|
|
if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
|
|
return Record;
|
|
|
|
if (EnteringContext) {
|
|
if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
|
|
return nullptr;
|
|
const Type *NNSType = NNS.getAsType();
|
|
|
|
// Look through type alias templates, per C++0x [temp.dep.type]p1.
|
|
NNSType = Context.getCanonicalType(NNSType);
|
|
if (const auto *SpecType =
|
|
dyn_cast<TemplateSpecializationType>(NNSType)) {
|
|
// We are entering the context of the nested name specifier, so try to
|
|
// match the nested name specifier to either a primary class template
|
|
// or a class template partial specialization.
|
|
if (ClassTemplateDecl *ClassTemplate =
|
|
dyn_cast_or_null<ClassTemplateDecl>(
|
|
SpecType->getTemplateName().getAsTemplateDecl())) {
|
|
// FIXME: The fallback on the search of partial
|
|
// specialization using ContextType should be eventually removed since
|
|
// it doesn't handle the case of constrained template parameters
|
|
// correctly. Currently removing this fallback would change the
|
|
// diagnostic output for invalid code in a number of tests.
|
|
ClassTemplatePartialSpecializationDecl *PartialSpec = nullptr;
|
|
ArrayRef<TemplateParameterList *> TemplateParamLists =
|
|
SS.getTemplateParamLists();
|
|
if (!TemplateParamLists.empty()) {
|
|
unsigned Depth = ClassTemplate->getTemplateParameters()->getDepth();
|
|
auto L = find_if(TemplateParamLists,
|
|
[Depth](TemplateParameterList *TPL) {
|
|
return TPL->getDepth() == Depth;
|
|
});
|
|
if (L != TemplateParamLists.end()) {
|
|
void *Pos = nullptr;
|
|
PartialSpec = ClassTemplate->findPartialSpecialization(
|
|
SpecType->template_arguments(), *L, Pos);
|
|
}
|
|
} else {
|
|
PartialSpec =
|
|
ClassTemplate->findPartialSpecialization(QualType(SpecType, 0));
|
|
}
|
|
|
|
if (PartialSpec) {
|
|
// A declaration of the partial specialization must be visible.
|
|
// We can always recover here, because this only happens when we're
|
|
// entering the context, and that can't happen in a SFINAE context.
|
|
assert(!isSFINAEContext() && "partial specialization scope "
|
|
"specifier in SFINAE context?");
|
|
if (PartialSpec->hasDefinition() &&
|
|
!hasReachableDefinition(PartialSpec))
|
|
diagnoseMissingImport(SS.getLastQualifierNameLoc(), PartialSpec,
|
|
MissingImportKind::PartialSpecialization,
|
|
true);
|
|
return PartialSpec;
|
|
}
|
|
|
|
// If the type of the nested name specifier is the same as the
|
|
// injected class name of the named class template, we're entering
|
|
// into that class template definition.
|
|
CanQualType Injected =
|
|
ClassTemplate->getCanonicalInjectedSpecializationType(Context);
|
|
if (Context.hasSameType(Injected, QualType(SpecType, 0)))
|
|
return ClassTemplate->getTemplatedDecl();
|
|
}
|
|
} else if (const auto *RecordT = dyn_cast<RecordType>(NNSType)) {
|
|
// The nested name specifier refers to a member of a class template.
|
|
return RecordT->getOriginalDecl()->getDefinitionOrSelf();
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
switch (NNS.getKind()) {
|
|
case NestedNameSpecifier::Kind::Namespace:
|
|
return const_cast<NamespaceDecl *>(
|
|
NNS.getAsNamespaceAndPrefix().Namespace->getNamespace());
|
|
|
|
case NestedNameSpecifier::Kind::Type: {
|
|
auto *TD = NNS.getAsType()->getAsTagDecl();
|
|
assert(TD && "Non-tag type in nested-name-specifier");
|
|
return TD;
|
|
}
|
|
|
|
case NestedNameSpecifier::Kind::Global:
|
|
return Context.getTranslationUnitDecl();
|
|
|
|
case NestedNameSpecifier::Kind::MicrosoftSuper:
|
|
return NNS.getAsMicrosoftSuper();
|
|
|
|
case NestedNameSpecifier::Kind::Null:
|
|
llvm_unreachable("unexpected null nested name specifier");
|
|
}
|
|
|
|
llvm_unreachable("Invalid NestedNameSpecifier::Kind!");
|
|
}
|
|
|
|
bool Sema::isDependentScopeSpecifier(const CXXScopeSpec &SS) {
|
|
if (!SS.isSet() || SS.isInvalid())
|
|
return false;
|
|
|
|
return SS.getScopeRep().isDependent();
|
|
}
|
|
|
|
CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier NNS) {
|
|
assert(getLangOpts().CPlusPlus && "Only callable in C++");
|
|
assert(NNS.isDependent() && "Only dependent nested-name-specifier allowed");
|
|
|
|
if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
|
|
return nullptr;
|
|
|
|
QualType T = QualType(NNS.getAsType(), 0);
|
|
return ::getCurrentInstantiationOf(T, CurContext);
|
|
}
|
|
|
|
/// Require that the context specified by SS be complete.
|
|
///
|
|
/// If SS refers to a type, this routine checks whether the type is
|
|
/// complete enough (or can be made complete enough) for name lookup
|
|
/// into the DeclContext. A type that is not yet completed can be
|
|
/// considered "complete enough" if it is a class/struct/union/enum
|
|
/// that is currently being defined. Or, if we have a type that names
|
|
/// a class template specialization that is not a complete type, we
|
|
/// will attempt to instantiate that class template.
|
|
bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS,
|
|
DeclContext *DC) {
|
|
assert(DC && "given null context");
|
|
|
|
TagDecl *tag = dyn_cast<TagDecl>(DC);
|
|
|
|
// If this is a dependent type, then we consider it complete.
|
|
// FIXME: This is wrong; we should require a (visible) definition to
|
|
// exist in this case too.
|
|
if (!tag || tag->isDependentContext())
|
|
return false;
|
|
|
|
// Grab the tag definition, if there is one.
|
|
tag = tag->getDefinitionOrSelf();
|
|
|
|
// If we're currently defining this type, then lookup into the
|
|
// type is okay: don't complain that it isn't complete yet.
|
|
if (tag->isBeingDefined())
|
|
return false;
|
|
|
|
SourceLocation loc = SS.getLastQualifierNameLoc();
|
|
if (loc.isInvalid()) loc = SS.getRange().getBegin();
|
|
|
|
// The type must be complete.
|
|
if (RequireCompleteType(loc, Context.getCanonicalTagType(tag),
|
|
diag::err_incomplete_nested_name_spec,
|
|
SS.getRange())) {
|
|
SS.SetInvalid(SS.getRange());
|
|
return true;
|
|
}
|
|
|
|
if (auto *EnumD = dyn_cast<EnumDecl>(tag))
|
|
// Fixed enum types and scoped enum instantiations are complete, but they
|
|
// aren't valid as scopes until we see or instantiate their definition.
|
|
return RequireCompleteEnumDecl(EnumD, loc, &SS);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Require that the EnumDecl is completed with its enumerators defined or
|
|
/// instantiated. SS, if provided, is the ScopeRef parsed.
|
|
///
|
|
bool Sema::RequireCompleteEnumDecl(EnumDecl *EnumD, SourceLocation L,
|
|
CXXScopeSpec *SS) {
|
|
if (EnumD->isCompleteDefinition()) {
|
|
// If we know about the definition but it is not visible, complain.
|
|
NamedDecl *SuggestedDef = nullptr;
|
|
if (!hasReachableDefinition(EnumD, &SuggestedDef,
|
|
/*OnlyNeedComplete*/ false)) {
|
|
// If the user is going to see an error here, recover by making the
|
|
// definition visible.
|
|
bool TreatAsComplete = !isSFINAEContext();
|
|
diagnoseMissingImport(L, SuggestedDef, MissingImportKind::Definition,
|
|
/*Recover*/ TreatAsComplete);
|
|
return !TreatAsComplete;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Try to instantiate the definition, if this is a specialization of an
|
|
// enumeration temploid.
|
|
if (EnumDecl *Pattern = EnumD->getInstantiatedFromMemberEnum()) {
|
|
MemberSpecializationInfo *MSI = EnumD->getMemberSpecializationInfo();
|
|
if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization) {
|
|
if (InstantiateEnum(L, EnumD, Pattern,
|
|
getTemplateInstantiationArgs(EnumD),
|
|
TSK_ImplicitInstantiation)) {
|
|
if (SS)
|
|
SS->SetInvalid(SS->getRange());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (SS) {
|
|
Diag(L, diag::err_incomplete_nested_name_spec)
|
|
<< Context.getCanonicalTagType(EnumD) << SS->getRange();
|
|
SS->SetInvalid(SS->getRange());
|
|
} else {
|
|
Diag(L, diag::err_incomplete_enum) << Context.getCanonicalTagType(EnumD);
|
|
Diag(EnumD->getLocation(), diag::note_declared_at);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Sema::ActOnCXXGlobalScopeSpecifier(SourceLocation CCLoc,
|
|
CXXScopeSpec &SS) {
|
|
SS.MakeGlobal(Context, CCLoc);
|
|
return false;
|
|
}
|
|
|
|
bool Sema::ActOnSuperScopeSpecifier(SourceLocation SuperLoc,
|
|
SourceLocation ColonColonLoc,
|
|
CXXScopeSpec &SS) {
|
|
if (getCurLambda()) {
|
|
Diag(SuperLoc, diag::err_super_in_lambda_unsupported);
|
|
return true;
|
|
}
|
|
|
|
CXXRecordDecl *RD = nullptr;
|
|
for (Scope *S = getCurScope(); S; S = S->getParent()) {
|
|
if (S->isFunctionScope()) {
|
|
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(S->getEntity()))
|
|
RD = MD->getParent();
|
|
break;
|
|
}
|
|
if (S->isClassScope()) {
|
|
RD = cast<CXXRecordDecl>(S->getEntity());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!RD) {
|
|
Diag(SuperLoc, diag::err_invalid_super_scope);
|
|
return true;
|
|
} else if (RD->getNumBases() == 0) {
|
|
Diag(SuperLoc, diag::err_no_base_classes) << RD->getName();
|
|
return true;
|
|
}
|
|
|
|
SS.MakeMicrosoftSuper(Context, RD, SuperLoc, ColonColonLoc);
|
|
return false;
|
|
}
|
|
|
|
bool Sema::isAcceptableNestedNameSpecifier(const NamedDecl *SD,
|
|
bool *IsExtension) {
|
|
if (!SD)
|
|
return false;
|
|
|
|
SD = SD->getUnderlyingDecl();
|
|
|
|
// Namespace and namespace aliases are fine.
|
|
if (isa<NamespaceDecl>(SD))
|
|
return true;
|
|
|
|
if (!isa<TypeDecl>(SD))
|
|
return false;
|
|
|
|
// Determine whether we have a class (or, in C++11, an enum) or
|
|
// a typedef thereof. If so, build the nested-name-specifier.
|
|
if (const TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(SD)) {
|
|
if (TD->getUnderlyingType()->isRecordType())
|
|
return true;
|
|
if (TD->getUnderlyingType()->isEnumeralType()) {
|
|
if (Context.getLangOpts().CPlusPlus11)
|
|
return true;
|
|
if (IsExtension)
|
|
*IsExtension = true;
|
|
}
|
|
} else if (isa<RecordDecl>(SD)) {
|
|
return true;
|
|
} else if (isa<EnumDecl>(SD)) {
|
|
if (Context.getLangOpts().CPlusPlus11)
|
|
return true;
|
|
if (IsExtension)
|
|
*IsExtension = true;
|
|
}
|
|
if (auto *TD = dyn_cast<TagDecl>(SD)) {
|
|
if (TD->isDependentType())
|
|
return true;
|
|
} else if (Context.getCanonicalTypeDeclType(cast<TypeDecl>(SD))
|
|
->isDependentType()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
NamedDecl *Sema::FindFirstQualifierInScope(Scope *S, NestedNameSpecifier NNS) {
|
|
if (!S)
|
|
return nullptr;
|
|
|
|
while (NNS.getKind() == NestedNameSpecifier::Kind::Type) {
|
|
const Type *T = NNS.getAsType();
|
|
if ((NNS = T->getPrefix()))
|
|
continue;
|
|
|
|
const auto *DNT = dyn_cast<DependentNameType>(T);
|
|
if (!DNT)
|
|
break;
|
|
|
|
LookupResult Found(*this, DNT->getIdentifier(), SourceLocation(),
|
|
LookupNestedNameSpecifierName);
|
|
LookupName(Found, S);
|
|
assert(!Found.isAmbiguous() && "Cannot handle ambiguities here yet");
|
|
|
|
if (!Found.isSingleResult())
|
|
return nullptr;
|
|
|
|
NamedDecl *Result = Found.getFoundDecl();
|
|
if (isAcceptableNestedNameSpecifier(Result))
|
|
return Result;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
namespace {
|
|
|
|
// Callback to only accept typo corrections that can be a valid C++ member
|
|
// initializer: either a non-static field member or a base class.
|
|
class NestedNameSpecifierValidatorCCC final
|
|
: public CorrectionCandidateCallback {
|
|
public:
|
|
explicit NestedNameSpecifierValidatorCCC(Sema &SRef)
|
|
: SRef(SRef) {}
|
|
|
|
bool ValidateCandidate(const TypoCorrection &candidate) override {
|
|
return SRef.isAcceptableNestedNameSpecifier(candidate.getCorrectionDecl());
|
|
}
|
|
|
|
std::unique_ptr<CorrectionCandidateCallback> clone() override {
|
|
return std::make_unique<NestedNameSpecifierValidatorCCC>(*this);
|
|
}
|
|
|
|
private:
|
|
Sema &SRef;
|
|
};
|
|
|
|
}
|
|
|
|
bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
|
|
bool EnteringContext, CXXScopeSpec &SS,
|
|
NamedDecl *ScopeLookupResult,
|
|
bool ErrorRecoveryLookup,
|
|
bool *IsCorrectedToColon,
|
|
bool OnlyNamespace) {
|
|
if (IdInfo.Identifier->isEditorPlaceholder())
|
|
return true;
|
|
LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
|
|
OnlyNamespace ? LookupNamespaceName
|
|
: LookupNestedNameSpecifierName);
|
|
QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType);
|
|
|
|
// Determine where to perform name lookup
|
|
DeclContext *LookupCtx = nullptr;
|
|
bool isDependent = false;
|
|
if (IsCorrectedToColon)
|
|
*IsCorrectedToColon = false;
|
|
if (!ObjectType.isNull()) {
|
|
// This nested-name-specifier occurs in a member access expression, e.g.,
|
|
// x->B::f, and we are looking into the type of the object.
|
|
assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
|
|
LookupCtx = computeDeclContext(ObjectType);
|
|
isDependent = ObjectType->isDependentType();
|
|
} else if (SS.isSet()) {
|
|
// This nested-name-specifier occurs after another nested-name-specifier,
|
|
// so look into the context associated with the prior nested-name-specifier.
|
|
LookupCtx = computeDeclContext(SS, EnteringContext);
|
|
isDependent = isDependentScopeSpecifier(SS);
|
|
Found.setContextRange(SS.getRange());
|
|
}
|
|
|
|
bool ObjectTypeSearchedInScope = false;
|
|
if (LookupCtx) {
|
|
// Perform "qualified" name lookup into the declaration context we
|
|
// computed, which is either the type of the base of a member access
|
|
// expression or the declaration context associated with a prior
|
|
// nested-name-specifier.
|
|
|
|
// The declaration context must be complete.
|
|
if (!LookupCtx->isDependentContext() &&
|
|
RequireCompleteDeclContext(SS, LookupCtx))
|
|
return true;
|
|
|
|
LookupQualifiedName(Found, LookupCtx);
|
|
|
|
if (!ObjectType.isNull() && Found.empty()) {
|
|
// C++ [basic.lookup.classref]p4:
|
|
// If the id-expression in a class member access is a qualified-id of
|
|
// the form
|
|
//
|
|
// class-name-or-namespace-name::...
|
|
//
|
|
// the class-name-or-namespace-name following the . or -> operator is
|
|
// looked up both in the context of the entire postfix-expression and in
|
|
// the scope of the class of the object expression. If the name is found
|
|
// only in the scope of the class of the object expression, the name
|
|
// shall refer to a class-name. If the name is found only in the
|
|
// context of the entire postfix-expression, the name shall refer to a
|
|
// class-name or namespace-name. [...]
|
|
//
|
|
// Qualified name lookup into a class will not find a namespace-name,
|
|
// so we do not need to diagnose that case specifically. However,
|
|
// this qualified name lookup may find nothing. In that case, perform
|
|
// unqualified name lookup in the given scope (if available) or
|
|
// reconstruct the result from when name lookup was performed at template
|
|
// definition time.
|
|
if (S)
|
|
LookupName(Found, S);
|
|
else if (ScopeLookupResult)
|
|
Found.addDecl(ScopeLookupResult);
|
|
|
|
ObjectTypeSearchedInScope = true;
|
|
}
|
|
} else if (!isDependent) {
|
|
// Perform unqualified name lookup in the current scope.
|
|
LookupName(Found, S);
|
|
}
|
|
|
|
if (Found.isAmbiguous())
|
|
return true;
|
|
|
|
// If we performed lookup into a dependent context and did not find anything,
|
|
// that's fine: just build a dependent nested-name-specifier.
|
|
if (Found.empty() && isDependent &&
|
|
!(LookupCtx && LookupCtx->isRecord() &&
|
|
(!cast<CXXRecordDecl>(LookupCtx)->hasDefinition() ||
|
|
!cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()))) {
|
|
// Don't speculate if we're just trying to improve error recovery.
|
|
if (ErrorRecoveryLookup)
|
|
return true;
|
|
|
|
// We were not able to compute the declaration context for a dependent
|
|
// base object type or prior nested-name-specifier, so this
|
|
// nested-name-specifier refers to an unknown specialization. Just build
|
|
// a dependent nested-name-specifier.
|
|
|
|
TypeLocBuilder TLB;
|
|
|
|
QualType DTN = Context.getDependentNameType(
|
|
ElaboratedTypeKeyword::None, SS.getScopeRep(), IdInfo.Identifier);
|
|
auto DTNL = TLB.push<DependentNameTypeLoc>(DTN);
|
|
DTNL.setElaboratedKeywordLoc(SourceLocation());
|
|
DTNL.setNameLoc(IdInfo.IdentifierLoc);
|
|
DTNL.setQualifierLoc(SS.getWithLocInContext(Context));
|
|
|
|
SS.clear();
|
|
SS.Make(Context, TLB.getTypeLocInContext(Context, DTN), IdInfo.CCLoc);
|
|
return false;
|
|
}
|
|
|
|
if (Found.empty() && !ErrorRecoveryLookup) {
|
|
// If identifier is not found as class-name-or-namespace-name, but is found
|
|
// as other entity, don't look for typos.
|
|
LookupResult R(*this, Found.getLookupNameInfo(), LookupOrdinaryName);
|
|
if (LookupCtx)
|
|
LookupQualifiedName(R, LookupCtx);
|
|
else if (S && !isDependent)
|
|
LookupName(R, S);
|
|
if (!R.empty()) {
|
|
// Don't diagnose problems with this speculative lookup.
|
|
R.suppressDiagnostics();
|
|
// The identifier is found in ordinary lookup. If correction to colon is
|
|
// allowed, suggest replacement to ':'.
|
|
if (IsCorrectedToColon) {
|
|
*IsCorrectedToColon = true;
|
|
Diag(IdInfo.CCLoc, diag::err_nested_name_spec_is_not_class)
|
|
<< IdInfo.Identifier << getLangOpts().CPlusPlus
|
|
<< FixItHint::CreateReplacement(IdInfo.CCLoc, ":");
|
|
if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
|
|
Diag(ND->getLocation(), diag::note_declared_at);
|
|
return true;
|
|
}
|
|
// Replacement '::' -> ':' is not allowed, just issue respective error.
|
|
Diag(R.getNameLoc(), OnlyNamespace
|
|
? unsigned(diag::err_expected_namespace_name)
|
|
: unsigned(diag::err_expected_class_or_namespace))
|
|
<< IdInfo.Identifier << getLangOpts().CPlusPlus;
|
|
if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
|
|
Diag(ND->getLocation(), diag::note_entity_declared_at)
|
|
<< IdInfo.Identifier;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (Found.empty() && !ErrorRecoveryLookup && !getLangOpts().MSVCCompat) {
|
|
// We haven't found anything, and we're not recovering from a
|
|
// different kind of error, so look for typos.
|
|
DeclarationName Name = Found.getLookupName();
|
|
Found.clear();
|
|
NestedNameSpecifierValidatorCCC CCC(*this);
|
|
if (TypoCorrection Corrected = CorrectTypo(
|
|
Found.getLookupNameInfo(), Found.getLookupKind(), S, &SS, CCC,
|
|
CorrectTypoKind::ErrorRecovery, LookupCtx, EnteringContext)) {
|
|
if (LookupCtx) {
|
|
bool DroppedSpecifier =
|
|
Corrected.WillReplaceSpecifier() &&
|
|
Name.getAsString() == Corrected.getAsString(getLangOpts());
|
|
if (DroppedSpecifier)
|
|
SS.clear();
|
|
diagnoseTypo(Corrected, PDiag(diag::err_no_member_suggest)
|
|
<< Name << LookupCtx << DroppedSpecifier
|
|
<< SS.getRange());
|
|
} else
|
|
diagnoseTypo(Corrected, PDiag(diag::err_undeclared_var_use_suggest)
|
|
<< Name);
|
|
|
|
if (Corrected.getCorrectionSpecifier())
|
|
SS.MakeTrivial(Context, Corrected.getCorrectionSpecifier(),
|
|
SourceRange(Found.getNameLoc()));
|
|
|
|
if (NamedDecl *ND = Corrected.getFoundDecl())
|
|
Found.addDecl(ND);
|
|
Found.setLookupName(Corrected.getCorrection());
|
|
} else {
|
|
Found.setLookupName(IdInfo.Identifier);
|
|
}
|
|
}
|
|
|
|
NamedDecl *SD =
|
|
Found.isSingleResult() ? Found.getRepresentativeDecl() : nullptr;
|
|
bool IsExtension = false;
|
|
bool AcceptSpec = isAcceptableNestedNameSpecifier(SD, &IsExtension);
|
|
if (!AcceptSpec && IsExtension) {
|
|
AcceptSpec = true;
|
|
Diag(IdInfo.IdentifierLoc, diag::ext_nested_name_spec_is_enum);
|
|
}
|
|
if (AcceptSpec) {
|
|
if (!ObjectType.isNull() && !ObjectTypeSearchedInScope &&
|
|
!getLangOpts().CPlusPlus11) {
|
|
// C++03 [basic.lookup.classref]p4:
|
|
// [...] If the name is found in both contexts, the
|
|
// class-name-or-namespace-name shall refer to the same entity.
|
|
//
|
|
// We already found the name in the scope of the object. Now, look
|
|
// into the current scope (the scope of the postfix-expression) to
|
|
// see if we can find the same name there. As above, if there is no
|
|
// scope, reconstruct the result from the template instantiation itself.
|
|
//
|
|
// Note that C++11 does *not* perform this redundant lookup.
|
|
NamedDecl *OuterDecl;
|
|
if (S) {
|
|
LookupResult FoundOuter(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
|
|
LookupNestedNameSpecifierName);
|
|
LookupName(FoundOuter, S);
|
|
OuterDecl = FoundOuter.getAsSingle<NamedDecl>();
|
|
} else
|
|
OuterDecl = ScopeLookupResult;
|
|
|
|
if (isAcceptableNestedNameSpecifier(OuterDecl) &&
|
|
OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() &&
|
|
(!isa<TypeDecl>(OuterDecl) || !isa<TypeDecl>(SD) ||
|
|
!Context.hasSameType(
|
|
Context.getCanonicalTypeDeclType(cast<TypeDecl>(OuterDecl)),
|
|
Context.getCanonicalTypeDeclType(cast<TypeDecl>(SD))))) {
|
|
if (ErrorRecoveryLookup)
|
|
return true;
|
|
|
|
Diag(IdInfo.IdentifierLoc,
|
|
diag::err_nested_name_member_ref_lookup_ambiguous)
|
|
<< IdInfo.Identifier;
|
|
Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type)
|
|
<< ObjectType;
|
|
Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope);
|
|
|
|
// Fall through so that we'll pick the name we found in the object
|
|
// type, since that's probably what the user wanted anyway.
|
|
}
|
|
}
|
|
|
|
if (auto *TD = dyn_cast_or_null<TypedefNameDecl>(SD))
|
|
MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
|
|
|
|
// If we're just performing this lookup for error-recovery purposes,
|
|
// don't extend the nested-name-specifier. Just return now.
|
|
if (ErrorRecoveryLookup)
|
|
return false;
|
|
|
|
// The use of a nested name specifier may trigger deprecation warnings.
|
|
DiagnoseUseOfDecl(SD, IdInfo.CCLoc);
|
|
|
|
if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD)) {
|
|
SS.Extend(Context, Namespace, IdInfo.IdentifierLoc, IdInfo.CCLoc);
|
|
return false;
|
|
}
|
|
|
|
if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD)) {
|
|
SS.Extend(Context, Alias, IdInfo.IdentifierLoc, IdInfo.CCLoc);
|
|
return false;
|
|
}
|
|
|
|
const auto *TD = cast<TypeDecl>(SD->getUnderlyingDecl());
|
|
if (isa<EnumDecl>(TD))
|
|
Diag(IdInfo.IdentifierLoc, diag::warn_cxx98_compat_enum_nested_name_spec);
|
|
|
|
QualType T;
|
|
TypeLocBuilder TLB;
|
|
if (const auto *USD = dyn_cast<UsingShadowDecl>(SD)) {
|
|
T = Context.getUsingType(ElaboratedTypeKeyword::None, SS.getScopeRep(),
|
|
USD);
|
|
TLB.push<UsingTypeLoc>(T).set(/*ElaboratedKeywordLoc=*/SourceLocation(),
|
|
SS.getWithLocInContext(Context),
|
|
IdInfo.IdentifierLoc);
|
|
} else if (const auto *Tag = dyn_cast<TagDecl>(TD)) {
|
|
T = Context.getTagType(ElaboratedTypeKeyword::None, SS.getScopeRep(), Tag,
|
|
/*OwnsTag=*/false);
|
|
auto TTL = TLB.push<TagTypeLoc>(T);
|
|
TTL.setElaboratedKeywordLoc(SourceLocation());
|
|
TTL.setQualifierLoc(SS.getWithLocInContext(SemaRef.Context));
|
|
TTL.setNameLoc(IdInfo.IdentifierLoc);
|
|
} else if (auto *TN = dyn_cast<TypedefNameDecl>(TD)) {
|
|
T = Context.getTypedefType(ElaboratedTypeKeyword::None, SS.getScopeRep(),
|
|
TN);
|
|
TLB.push<TypedefTypeLoc>(T).set(/*ElaboratedKeywordLoc=*/SourceLocation(),
|
|
SS.getWithLocInContext(SemaRef.Context),
|
|
IdInfo.IdentifierLoc);
|
|
} else if (auto *UD = dyn_cast<UnresolvedUsingTypenameDecl>(TD)) {
|
|
T = Context.getUnresolvedUsingType(ElaboratedTypeKeyword::None,
|
|
SS.getScopeRep(), UD);
|
|
TLB.push<UnresolvedUsingTypeLoc>(T).set(
|
|
/*ElaboratedKeywordLoc=*/SourceLocation(),
|
|
SS.getWithLocInContext(SemaRef.Context), IdInfo.IdentifierLoc);
|
|
} else {
|
|
assert(SS.isEmpty());
|
|
T = Context.getTypeDeclType(TD);
|
|
TLB.pushTypeSpec(T).setNameLoc(IdInfo.IdentifierLoc);
|
|
}
|
|
SS.clear();
|
|
SS.Make(Context, TLB.getTypeLocInContext(Context, T), IdInfo.CCLoc);
|
|
return false;
|
|
}
|
|
|
|
// Otherwise, we have an error case. If we don't want diagnostics, just
|
|
// return an error now.
|
|
if (ErrorRecoveryLookup)
|
|
return true;
|
|
|
|
// If we didn't find anything during our lookup, try again with
|
|
// ordinary name lookup, which can help us produce better error
|
|
// messages.
|
|
if (Found.empty()) {
|
|
Found.clear(LookupOrdinaryName);
|
|
LookupName(Found, S);
|
|
}
|
|
|
|
// In Microsoft mode, if we are within a templated function and we can't
|
|
// resolve Identifier, then extend the SS with Identifier. This will have
|
|
// the effect of resolving Identifier during template instantiation.
|
|
// The goal is to be able to resolve a function call whose
|
|
// nested-name-specifier is located inside a dependent base class.
|
|
// Example:
|
|
//
|
|
// class C {
|
|
// public:
|
|
// static void foo2() { }
|
|
// };
|
|
// template <class T> class A { public: typedef C D; };
|
|
//
|
|
// template <class T> class B : public A<T> {
|
|
// public:
|
|
// void foo() { D::foo2(); }
|
|
// };
|
|
if (getLangOpts().MSVCCompat) {
|
|
DeclContext *DC = LookupCtx ? LookupCtx : CurContext;
|
|
if (DC->isDependentContext() && DC->isFunctionOrMethod()) {
|
|
CXXRecordDecl *ContainingClass = dyn_cast<CXXRecordDecl>(DC->getParent());
|
|
if (ContainingClass && ContainingClass->hasAnyDependentBases()) {
|
|
Diag(IdInfo.IdentifierLoc,
|
|
diag::ext_undeclared_unqual_id_with_dependent_base)
|
|
<< IdInfo.Identifier << ContainingClass;
|
|
|
|
TypeLocBuilder TLB;
|
|
|
|
// Fake up a nested-name-specifier that starts with the
|
|
// injected-class-name of the enclosing class.
|
|
// FIXME: This should be done as part of an adjustment, so that this
|
|
// doesn't get confused with something written in source.
|
|
QualType Result =
|
|
Context.getTagType(ElaboratedTypeKeyword::None, SS.getScopeRep(),
|
|
ContainingClass, /*OwnsTag=*/false);
|
|
auto TTL = TLB.push<TagTypeLoc>(Result);
|
|
TTL.setElaboratedKeywordLoc(SourceLocation());
|
|
TTL.setQualifierLoc(SS.getWithLocInContext(Context));
|
|
TTL.setNameLoc(IdInfo.IdentifierLoc);
|
|
SS.Make(Context, TLB.getTypeLocInContext(Context, Result),
|
|
SourceLocation());
|
|
|
|
TLB.clear();
|
|
|
|
// Form a DependentNameType.
|
|
QualType DTN = Context.getDependentNameType(
|
|
ElaboratedTypeKeyword::None, SS.getScopeRep(), IdInfo.Identifier);
|
|
auto DTNL = TLB.push<DependentNameTypeLoc>(DTN);
|
|
DTNL.setElaboratedKeywordLoc(SourceLocation());
|
|
DTNL.setNameLoc(IdInfo.IdentifierLoc);
|
|
DTNL.setQualifierLoc(SS.getWithLocInContext(Context));
|
|
SS.clear();
|
|
SS.Make(Context, TLB.getTypeLocInContext(Context, DTN), IdInfo.CCLoc);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Found.empty()) {
|
|
if (TypeDecl *TD = Found.getAsSingle<TypeDecl>()) {
|
|
QualType T;
|
|
if (auto *TN = dyn_cast<TypedefNameDecl>(TD)) {
|
|
T = Context.getTypedefType(ElaboratedTypeKeyword::None,
|
|
SS.getScopeRep(), TN);
|
|
} else {
|
|
// FIXME: Enumerate the possibilities here.
|
|
assert(!isa<TagDecl>(TD));
|
|
assert(SS.isEmpty());
|
|
T = Context.getTypeDeclType(TD);
|
|
}
|
|
|
|
Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace)
|
|
<< T << getLangOpts().CPlusPlus;
|
|
} else if (Found.getAsSingle<TemplateDecl>()) {
|
|
ParsedType SuggestedType;
|
|
DiagnoseUnknownTypeName(IdInfo.Identifier, IdInfo.IdentifierLoc, S, &SS,
|
|
SuggestedType);
|
|
} else {
|
|
Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace)
|
|
<< IdInfo.Identifier << getLangOpts().CPlusPlus;
|
|
if (NamedDecl *ND = Found.getAsSingle<NamedDecl>())
|
|
Diag(ND->getLocation(), diag::note_entity_declared_at)
|
|
<< IdInfo.Identifier;
|
|
}
|
|
} else if (SS.isSet())
|
|
Diag(IdInfo.IdentifierLoc, diag::err_no_member) << IdInfo.Identifier
|
|
<< LookupCtx << SS.getRange();
|
|
else
|
|
Diag(IdInfo.IdentifierLoc, diag::err_undeclared_var_use)
|
|
<< IdInfo.Identifier;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
|
|
bool EnteringContext, CXXScopeSpec &SS,
|
|
bool *IsCorrectedToColon,
|
|
bool OnlyNamespace) {
|
|
if (SS.isInvalid())
|
|
return true;
|
|
|
|
return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
|
|
/*ScopeLookupResult=*/nullptr, false,
|
|
IsCorrectedToColon, OnlyNamespace);
|
|
}
|
|
|
|
bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
|
|
const DeclSpec &DS,
|
|
SourceLocation ColonColonLoc) {
|
|
if (SS.isInvalid() || DS.getTypeSpecType() == DeclSpec::TST_error)
|
|
return true;
|
|
|
|
assert(DS.getTypeSpecType() == DeclSpec::TST_decltype);
|
|
|
|
QualType T = BuildDecltypeType(DS.getRepAsExpr());
|
|
if (T.isNull())
|
|
return true;
|
|
|
|
if (!T->isDependentType() && !isa<TagType>(T.getCanonicalType())) {
|
|
Diag(DS.getTypeSpecTypeLoc(), diag::err_expected_class_or_namespace)
|
|
<< T << getLangOpts().CPlusPlus;
|
|
return true;
|
|
}
|
|
|
|
assert(SS.isEmpty());
|
|
|
|
TypeLocBuilder TLB;
|
|
DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
|
|
DecltypeTL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
|
|
DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
|
|
SS.Make(Context, TLB.getTypeLocInContext(Context, T), ColonColonLoc);
|
|
return false;
|
|
}
|
|
|
|
bool Sema::ActOnCXXNestedNameSpecifierIndexedPack(CXXScopeSpec &SS,
|
|
const DeclSpec &DS,
|
|
SourceLocation ColonColonLoc,
|
|
QualType Type) {
|
|
if (SS.isInvalid() || DS.getTypeSpecType() == DeclSpec::TST_error)
|
|
return true;
|
|
|
|
assert(DS.getTypeSpecType() == DeclSpec::TST_typename_pack_indexing);
|
|
|
|
if (Type.isNull())
|
|
return true;
|
|
|
|
assert(SS.isEmpty());
|
|
|
|
TypeLocBuilder TLB;
|
|
TLB.pushTrivial(getASTContext(),
|
|
cast<PackIndexingType>(Type.getTypePtr())->getPattern(),
|
|
DS.getBeginLoc());
|
|
PackIndexingTypeLoc PIT = TLB.push<PackIndexingTypeLoc>(Type);
|
|
PIT.setEllipsisLoc(DS.getEllipsisLoc());
|
|
SS.Make(Context, TLB.getTypeLocInContext(Context, Type), ColonColonLoc);
|
|
return false;
|
|
}
|
|
|
|
bool Sema::IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS,
|
|
NestedNameSpecInfo &IdInfo,
|
|
bool EnteringContext) {
|
|
if (SS.isInvalid())
|
|
return false;
|
|
|
|
return !BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
|
|
/*ScopeLookupResult=*/nullptr, true);
|
|
}
|
|
|
|
bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
|
CXXScopeSpec &SS,
|
|
SourceLocation TemplateKWLoc,
|
|
TemplateTy OpaqueTemplate,
|
|
SourceLocation TemplateNameLoc,
|
|
SourceLocation LAngleLoc,
|
|
ASTTemplateArgsPtr TemplateArgsIn,
|
|
SourceLocation RAngleLoc,
|
|
SourceLocation CCLoc,
|
|
bool EnteringContext) {
|
|
if (SS.isInvalid())
|
|
return true;
|
|
|
|
TemplateName Template = OpaqueTemplate.get();
|
|
|
|
// Translate the parser's template argument list in our AST format.
|
|
TemplateArgumentListInfo TemplateArgs(LAngleLoc, RAngleLoc);
|
|
translateTemplateArguments(TemplateArgsIn, TemplateArgs);
|
|
|
|
DependentTemplateName *DTN = Template.getAsDependentTemplateName();
|
|
if (DTN && DTN->getName().getIdentifier()) {
|
|
// Handle a dependent template specialization for which we cannot resolve
|
|
// the template name.
|
|
assert(DTN->getQualifier() == SS.getScopeRep());
|
|
QualType T = Context.getDependentTemplateSpecializationType(
|
|
ElaboratedTypeKeyword::None,
|
|
{SS.getScopeRep(), DTN->getName().getIdentifier(),
|
|
TemplateKWLoc.isValid()},
|
|
TemplateArgs.arguments());
|
|
|
|
// Create source-location information for this type.
|
|
TypeLocBuilder Builder;
|
|
DependentTemplateSpecializationTypeLoc SpecTL
|
|
= Builder.push<DependentTemplateSpecializationTypeLoc>(T);
|
|
SpecTL.setElaboratedKeywordLoc(SourceLocation());
|
|
SpecTL.setQualifierLoc(SS.getWithLocInContext(Context));
|
|
SpecTL.setTemplateKeywordLoc(TemplateKWLoc);
|
|
SpecTL.setTemplateNameLoc(TemplateNameLoc);
|
|
SpecTL.setLAngleLoc(LAngleLoc);
|
|
SpecTL.setRAngleLoc(RAngleLoc);
|
|
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
|
|
SpecTL.setArgLocInfo(I, TemplateArgs[I].getLocInfo());
|
|
|
|
SS.clear();
|
|
SS.Make(Context, Builder.getTypeLocInContext(Context, T), CCLoc);
|
|
return false;
|
|
}
|
|
|
|
// If we assumed an undeclared identifier was a template name, try to
|
|
// typo-correct it now.
|
|
if (Template.getAsAssumedTemplateName() &&
|
|
resolveAssumedTemplateNameAsType(S, Template, TemplateNameLoc))
|
|
return true;
|
|
|
|
TemplateDecl *TD = Template.getAsTemplateDecl();
|
|
if (Template.getAsOverloadedTemplate() || DTN ||
|
|
isa<FunctionTemplateDecl>(TD) || isa<VarTemplateDecl>(TD)) {
|
|
SourceRange R(TemplateNameLoc, RAngleLoc);
|
|
if (SS.getRange().isValid())
|
|
R.setBegin(SS.getRange().getBegin());
|
|
|
|
Diag(CCLoc, diag::err_non_type_template_in_nested_name_specifier)
|
|
<< isa_and_nonnull<VarTemplateDecl>(TD) << Template << R;
|
|
NoteAllFoundTemplates(Template);
|
|
return true;
|
|
}
|
|
|
|
// We were able to resolve the template name to an actual template.
|
|
// Build an appropriate nested-name-specifier.
|
|
QualType T = CheckTemplateIdType(ElaboratedTypeKeyword::None, Template,
|
|
TemplateNameLoc, TemplateArgs);
|
|
if (T.isNull())
|
|
return true;
|
|
|
|
// Alias template specializations can produce types which are not valid
|
|
// nested name specifiers.
|
|
if (!T->isDependentType() && !isa<TagType>(T.getCanonicalType())) {
|
|
Diag(TemplateNameLoc, diag::err_nested_name_spec_non_tag) << T;
|
|
NoteAllFoundTemplates(Template);
|
|
return true;
|
|
}
|
|
|
|
// Provide source-location information for the template specialization type.
|
|
TypeLocBuilder TLB;
|
|
TLB.push<TemplateSpecializationTypeLoc>(T).set(
|
|
/*ElaboratedKeywordLoc=*/SourceLocation(),
|
|
SS.getWithLocInContext(Context), TemplateKWLoc, TemplateNameLoc,
|
|
TemplateArgs);
|
|
|
|
SS.clear();
|
|
SS.Make(Context, TLB.getTypeLocInContext(Context, T), CCLoc);
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
/// A structure that stores a nested-name-specifier annotation,
|
|
/// including both the nested-name-specifier
|
|
struct NestedNameSpecifierAnnotation {
|
|
NestedNameSpecifier NNS = std::nullopt;
|
|
};
|
|
}
|
|
|
|
void *Sema::SaveNestedNameSpecifierAnnotation(CXXScopeSpec &SS) {
|
|
if (SS.isEmpty() || SS.isInvalid())
|
|
return nullptr;
|
|
|
|
void *Mem = Context.Allocate(
|
|
(sizeof(NestedNameSpecifierAnnotation) + SS.location_size()),
|
|
alignof(NestedNameSpecifierAnnotation));
|
|
NestedNameSpecifierAnnotation *Annotation
|
|
= new (Mem) NestedNameSpecifierAnnotation;
|
|
Annotation->NNS = SS.getScopeRep();
|
|
memcpy(Annotation + 1, SS.location_data(), SS.location_size());
|
|
return Annotation;
|
|
}
|
|
|
|
void Sema::RestoreNestedNameSpecifierAnnotation(void *AnnotationPtr,
|
|
SourceRange AnnotationRange,
|
|
CXXScopeSpec &SS) {
|
|
if (!AnnotationPtr) {
|
|
SS.SetInvalid(AnnotationRange);
|
|
return;
|
|
}
|
|
|
|
NestedNameSpecifierAnnotation *Annotation
|
|
= static_cast<NestedNameSpecifierAnnotation *>(AnnotationPtr);
|
|
SS.Adopt(NestedNameSpecifierLoc(Annotation->NNS, Annotation + 1));
|
|
}
|
|
|
|
bool Sema::ShouldEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
|
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
|
|
|
|
// Don't enter a declarator context when the current context is an Objective-C
|
|
// declaration.
|
|
if (isa<ObjCContainerDecl>(CurContext) || isa<ObjCMethodDecl>(CurContext))
|
|
return false;
|
|
|
|
// There are only two places a well-formed program may qualify a
|
|
// declarator: first, when defining a namespace or class member
|
|
// out-of-line, and second, when naming an explicitly-qualified
|
|
// friend function. The latter case is governed by
|
|
// C++03 [basic.lookup.unqual]p10:
|
|
// In a friend declaration naming a member function, a name used
|
|
// in the function declarator and not part of a template-argument
|
|
// in a template-id is first looked up in the scope of the member
|
|
// function's class. If it is not found, or if the name is part of
|
|
// a template-argument in a template-id, the look up is as
|
|
// described for unqualified names in the definition of the class
|
|
// granting friendship.
|
|
// i.e. we don't push a scope unless it's a class member.
|
|
|
|
switch (SS.getScopeRep().getKind()) {
|
|
case NestedNameSpecifier::Kind::Global:
|
|
case NestedNameSpecifier::Kind::Namespace:
|
|
// These are always namespace scopes. We never want to enter a
|
|
// namespace scope from anything but a file context.
|
|
return CurContext->getRedeclContext()->isFileContext();
|
|
|
|
case NestedNameSpecifier::Kind::Type:
|
|
case NestedNameSpecifier::Kind::MicrosoftSuper:
|
|
// These are never namespace scopes.
|
|
return true;
|
|
|
|
case NestedNameSpecifier::Kind::Null:
|
|
llvm_unreachable("unexpected null nested name specifier");
|
|
}
|
|
|
|
llvm_unreachable("Invalid NestedNameSpecifier::Kind!");
|
|
}
|
|
|
|
bool Sema::ActOnCXXEnterDeclaratorScope(Scope *S, CXXScopeSpec &SS) {
|
|
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
|
|
|
|
if (SS.isInvalid()) return true;
|
|
|
|
DeclContext *DC = computeDeclContext(SS, true);
|
|
if (!DC) return true;
|
|
|
|
// Before we enter a declarator's context, we need to make sure that
|
|
// it is a complete declaration context.
|
|
if (!DC->isDependentContext() && RequireCompleteDeclContext(SS, DC))
|
|
return true;
|
|
|
|
EnterDeclaratorContext(S, DC);
|
|
|
|
// Rebuild the nested name specifier for the new scope.
|
|
if (DC->isDependentContext())
|
|
RebuildNestedNameSpecifierInCurrentInstantiation(SS);
|
|
|
|
return false;
|
|
}
|
|
|
|
void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
|
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
|
|
if (SS.isInvalid())
|
|
return;
|
|
assert(!SS.isInvalid() && computeDeclContext(SS, true) &&
|
|
"exiting declarator scope we never really entered");
|
|
ExitDeclaratorContext(S);
|
|
}
|