Vendor import of clang trunk r303291:
https://llvm.org/svn/llvm-project/cfe/trunk@303291
This commit is contained in:
parent
2410013d93
commit
1ce0879276
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/clang/dist/; revision=318416 svn path=/vendor/clang/clang-trunk-r303291/; revision=318417; tag=vendor/clang/clang-trunk-r303291
@ -301,16 +301,6 @@ class NamedDecl : public Decl {
|
||||
using Decl::isModulePrivate;
|
||||
using Decl::setModulePrivate;
|
||||
|
||||
/// \brief Determine whether this declaration is hidden from name lookup.
|
||||
bool isHidden() const { return Hidden; }
|
||||
|
||||
/// \brief Set whether this declaration is hidden from name lookup.
|
||||
void setHidden(bool Hide) {
|
||||
assert((!Hide || isFromASTFile() || hasLocalOwningModuleStorage()) &&
|
||||
"declaration with no owning module can't be hidden");
|
||||
Hidden = Hide;
|
||||
}
|
||||
|
||||
/// \brief Determine whether this declaration is a C++ class member.
|
||||
bool isCXXClassMember() const {
|
||||
const DeclContext *DC = getDeclContext();
|
||||
|
@ -706,6 +706,20 @@ class LLVM_ALIGNAS(/*alignof(uint64_t)*/ 8) Decl {
|
||||
reinterpret_cast<Module **>(this)[-1] = M;
|
||||
}
|
||||
|
||||
Module *getOwningModule() const {
|
||||
return isFromASTFile() ? getImportedOwningModule() : getLocalOwningModule();
|
||||
}
|
||||
|
||||
/// \brief Determine whether this declaration is hidden from name lookup.
|
||||
bool isHidden() const { return Hidden; }
|
||||
|
||||
/// \brief Set whether this declaration is hidden from name lookup.
|
||||
void setHidden(bool Hide) {
|
||||
assert((!Hide || isFromASTFile() || hasLocalOwningModuleStorage()) &&
|
||||
"declaration with no owning module can't be hidden");
|
||||
Hidden = Hide;
|
||||
}
|
||||
|
||||
unsigned getIdentifierNamespace() const {
|
||||
return IdentifierNamespace;
|
||||
}
|
||||
|
@ -166,6 +166,11 @@ class LangOptions : public LangOptionsBase {
|
||||
return getCompilingModule() != CMK_None;
|
||||
}
|
||||
|
||||
/// Do we need to track the owning module for a local declaration?
|
||||
bool trackLocalOwningModule() const {
|
||||
return ModulesLocalVisibility;
|
||||
}
|
||||
|
||||
bool isSignedOverflowDefined() const {
|
||||
return getSignedOverflowBehavior() == SOB_Defined;
|
||||
}
|
||||
|
@ -865,7 +865,7 @@ class SourceManager : public RefCountedBase<SourceManager> {
|
||||
const FileEntry *NewFile);
|
||||
|
||||
/// \brief Returns true if the file contents have been overridden.
|
||||
bool isFileOverridden(const FileEntry *File) {
|
||||
bool isFileOverridden(const FileEntry *File) const {
|
||||
if (OverriddenFilesInfo) {
|
||||
if (OverriddenFilesInfo->OverriddenFilesWithBuffer.count(File))
|
||||
return true;
|
||||
|
@ -419,7 +419,6 @@ class ASTUnit : public ModuleLoader {
|
||||
|
||||
explicit ASTUnit(bool MainFileIsAST);
|
||||
|
||||
void CleanTemporaryFiles();
|
||||
bool Parse(std::shared_ptr<PCHContainerOperations> PCHContainerOps,
|
||||
std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer);
|
||||
|
||||
@ -530,11 +529,6 @@ class ASTUnit : public ModuleLoader {
|
||||
ASTMutationListener *getASTMutationListener();
|
||||
ASTDeserializationListener *getDeserializationListener();
|
||||
|
||||
/// \brief Add a temporary file that the ASTUnit depends on.
|
||||
///
|
||||
/// This file will be erased when the ASTUnit is destroyed.
|
||||
void addTemporaryFile(StringRef TempFile);
|
||||
|
||||
bool getOnlyLocalDecls() const { return OnlyLocalDecls; }
|
||||
|
||||
bool getOwnsRemappedFileBuffers() const { return OwnsRemappedFileBuffers; }
|
||||
|
@ -1467,11 +1467,9 @@ class Sema {
|
||||
|
||||
VisibleModuleSet VisibleModules;
|
||||
|
||||
Module *CachedFakeTopLevelModule;
|
||||
|
||||
public:
|
||||
/// \brief Get the module owning an entity.
|
||||
Module *getOwningModule(Decl *Entity);
|
||||
Module *getOwningModule(Decl *Entity) { return Entity->getOwningModule(); }
|
||||
|
||||
/// \brief Make a merged definition of an existing hidden definition \p ND
|
||||
/// visible at the specified location.
|
||||
|
@ -1038,10 +1038,10 @@ void ASTDumper::dumpDecl(const Decl *D) {
|
||||
dumpSourceRange(D->getSourceRange());
|
||||
OS << ' ';
|
||||
dumpLocation(D->getLocation());
|
||||
if (Module *M = D->getImportedOwningModule())
|
||||
if (D->isFromASTFile())
|
||||
OS << " imported";
|
||||
if (Module *M = D->getOwningModule())
|
||||
OS << " in " << M->getFullModuleName();
|
||||
else if (Module *M = D->getLocalOwningModule())
|
||||
OS << " in (local) " << M->getFullModuleName();
|
||||
if (auto *ND = dyn_cast<NamedDecl>(D))
|
||||
for (Module *M : D->getASTContext().getModulesWithMergedDefinition(
|
||||
const_cast<NamedDecl *>(ND)))
|
||||
|
@ -47,9 +47,7 @@ bool Decl::isOutOfLine() const {
|
||||
|
||||
TranslationUnitDecl::TranslationUnitDecl(ASTContext &ctx)
|
||||
: Decl(TranslationUnit, nullptr, SourceLocation()),
|
||||
DeclContext(TranslationUnit), Ctx(ctx), AnonymousNamespace(nullptr) {
|
||||
Hidden = Ctx.getLangOpts().ModulesLocalVisibility;
|
||||
}
|
||||
DeclContext(TranslationUnit), Ctx(ctx), AnonymousNamespace(nullptr) {}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// NamedDecl Implementation
|
||||
|
@ -75,7 +75,7 @@ void *Decl::operator new(std::size_t Size, const ASTContext &Ctx,
|
||||
assert(!Parent || &Parent->getParentASTContext() == &Ctx);
|
||||
// With local visibility enabled, we track the owning module even for local
|
||||
// declarations.
|
||||
if (Ctx.getLangOpts().ModulesLocalVisibility) {
|
||||
if (Ctx.getLangOpts().trackLocalOwningModule()) {
|
||||
// Ensure required alignment of the resulting object by adding extra
|
||||
// padding at the start if required.
|
||||
size_t ExtraAlign =
|
||||
@ -83,7 +83,9 @@ void *Decl::operator new(std::size_t Size, const ASTContext &Ctx,
|
||||
char *Buffer = reinterpret_cast<char *>(
|
||||
::operator new(ExtraAlign + sizeof(Module *) + Size + Extra, Ctx));
|
||||
Buffer += ExtraAlign;
|
||||
return new (Buffer) Module*(nullptr) + 1;
|
||||
auto *ParentModule =
|
||||
Parent ? cast<Decl>(Parent)->getOwningModule() : nullptr;
|
||||
return new (Buffer) Module*(ParentModule) + 1;
|
||||
}
|
||||
return ::operator new(Size + Extra, Ctx);
|
||||
}
|
||||
@ -94,7 +96,7 @@ Module *Decl::getOwningModuleSlow() const {
|
||||
}
|
||||
|
||||
bool Decl::hasLocalOwningModuleStorage() const {
|
||||
return getASTContext().getLangOpts().ModulesLocalVisibility;
|
||||
return getASTContext().getLangOpts().trackLocalOwningModule();
|
||||
}
|
||||
|
||||
const char *Decl::getDeclKindName() const {
|
||||
@ -273,6 +275,8 @@ void Decl::setLexicalDeclContext(DeclContext *DC) {
|
||||
getMultipleDC()->LexicalDC = DC;
|
||||
}
|
||||
Hidden = cast<Decl>(DC)->Hidden;
|
||||
if (Hidden && !isFromASTFile() && hasLocalOwningModuleStorage())
|
||||
setLocalOwningModule(cast<Decl>(DC)->getOwningModule());
|
||||
}
|
||||
|
||||
void Decl::setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC,
|
||||
|
@ -81,7 +81,35 @@ void ODRHash::AddDeclarationName(DeclarationName Name) {
|
||||
}
|
||||
}
|
||||
|
||||
void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {}
|
||||
void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {
|
||||
assert(NNS && "Expecting non-null pointer.");
|
||||
const auto *Prefix = NNS->getPrefix();
|
||||
AddBoolean(Prefix);
|
||||
if (Prefix) {
|
||||
AddNestedNameSpecifier(Prefix);
|
||||
}
|
||||
auto Kind = NNS->getKind();
|
||||
ID.AddInteger(Kind);
|
||||
switch (Kind) {
|
||||
case NestedNameSpecifier::Identifier:
|
||||
AddIdentifierInfo(NNS->getAsIdentifier());
|
||||
break;
|
||||
case NestedNameSpecifier::Namespace:
|
||||
AddDecl(NNS->getAsNamespace());
|
||||
break;
|
||||
case NestedNameSpecifier::NamespaceAlias:
|
||||
AddDecl(NNS->getAsNamespaceAlias());
|
||||
break;
|
||||
case NestedNameSpecifier::TypeSpec:
|
||||
case NestedNameSpecifier::TypeSpecWithTemplate:
|
||||
AddType(NNS->getAsType());
|
||||
break;
|
||||
case NestedNameSpecifier::Global:
|
||||
case NestedNameSpecifier::Super:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ODRHash::AddTemplateName(TemplateName Name) {}
|
||||
void ODRHash::AddTemplateArgument(TemplateArgument TA) {}
|
||||
void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {}
|
||||
@ -335,6 +363,20 @@ class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> {
|
||||
Hash.AddQualType(T);
|
||||
}
|
||||
|
||||
void AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {
|
||||
Hash.AddBoolean(NNS);
|
||||
if (NNS) {
|
||||
Hash.AddNestedNameSpecifier(NNS);
|
||||
}
|
||||
}
|
||||
|
||||
void AddIdentifierInfo(const IdentifierInfo *II) {
|
||||
Hash.AddBoolean(II);
|
||||
if (II) {
|
||||
Hash.AddIdentifierInfo(II);
|
||||
}
|
||||
}
|
||||
|
||||
void VisitQualifiers(Qualifiers Quals) {
|
||||
ID.AddInteger(Quals.getAsOpaqueValue());
|
||||
}
|
||||
@ -414,6 +456,42 @@ class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> {
|
||||
AddQualType(T->getDecl()->getUnderlyingType().getCanonicalType());
|
||||
VisitType(T);
|
||||
}
|
||||
|
||||
void VisitTagType(const TagType *T) {
|
||||
AddDecl(T->getDecl());
|
||||
VisitType(T);
|
||||
}
|
||||
|
||||
void VisitRecordType(const RecordType *T) { VisitTagType(T); }
|
||||
void VisitEnumType(const EnumType *T) { VisitTagType(T); }
|
||||
|
||||
void VisitTypeWithKeyword(const TypeWithKeyword *T) {
|
||||
ID.AddInteger(T->getKeyword());
|
||||
VisitType(T);
|
||||
};
|
||||
|
||||
void VisitDependentNameType(const DependentNameType *T) {
|
||||
AddNestedNameSpecifier(T->getQualifier());
|
||||
AddIdentifierInfo(T->getIdentifier());
|
||||
VisitTypeWithKeyword(T);
|
||||
}
|
||||
|
||||
void VisitDependentTemplateSpecializationType(
|
||||
const DependentTemplateSpecializationType *T) {
|
||||
AddIdentifierInfo(T->getIdentifier());
|
||||
AddNestedNameSpecifier(T->getQualifier());
|
||||
ID.AddInteger(T->getNumArgs());
|
||||
for (const auto &TA : T->template_arguments()) {
|
||||
Hash.AddTemplateArgument(TA);
|
||||
}
|
||||
VisitTypeWithKeyword(T);
|
||||
}
|
||||
|
||||
void VisitElaboratedType(const ElaboratedType *T) {
|
||||
AddNestedNameSpecifier(T->getQualifier());
|
||||
AddQualType(T->getNamedType());
|
||||
VisitTypeWithKeyword(T);
|
||||
}
|
||||
};
|
||||
|
||||
void ODRHash::AddType(const Type *T) {
|
||||
|
@ -2860,7 +2860,7 @@ void CGDebugInfo::collectFunctionDeclProps(GlobalDecl GD, llvm::DIFile *Unit,
|
||||
|
||||
if (DebugKind >= codegenoptions::LimitedDebugInfo) {
|
||||
if (const NamespaceDecl *NSDecl =
|
||||
dyn_cast_or_null<NamespaceDecl>(FD->getLexicalDeclContext()))
|
||||
dyn_cast_or_null<NamespaceDecl>(FD->getDeclContext()))
|
||||
FDContext = getOrCreateNamespace(NSDecl);
|
||||
else if (const RecordDecl *RDecl =
|
||||
dyn_cast_or_null<RecordDecl>(FD->getDeclContext())) {
|
||||
|
@ -125,8 +125,15 @@ static bool findVCToolChainViaEnvironment(std::string &Path,
|
||||
continue;
|
||||
|
||||
// whatever/VC/bin --> old toolchain, VC dir is toolchain dir.
|
||||
if (llvm::sys::path::filename(PathEntry) == "bin") {
|
||||
llvm::StringRef ParentPath = llvm::sys::path::parent_path(PathEntry);
|
||||
llvm::StringRef TestPath = PathEntry;
|
||||
bool IsBin = llvm::sys::path::filename(TestPath).equals_lower("bin");
|
||||
if (!IsBin) {
|
||||
// Strip any architecture subdir like "amd64".
|
||||
TestPath = llvm::sys::path::parent_path(TestPath);
|
||||
IsBin = llvm::sys::path::filename(TestPath).equals_lower("bin");
|
||||
}
|
||||
if (IsBin) {
|
||||
llvm::StringRef ParentPath = llvm::sys::path::parent_path(TestPath);
|
||||
if (llvm::sys::path::filename(ParentPath) == "VC") {
|
||||
Path = ParentPath;
|
||||
IsVS2017OrNewer = false;
|
||||
|
@ -84,13 +84,6 @@ namespace {
|
||||
/// \brief The file in which the precompiled preamble is stored.
|
||||
std::string PreambleFile;
|
||||
|
||||
/// \brief Temporary files that should be removed when the ASTUnit is
|
||||
/// destroyed.
|
||||
SmallVector<std::string, 4> TemporaryFiles;
|
||||
|
||||
/// \brief Erase temporary files.
|
||||
void CleanTemporaryFiles();
|
||||
|
||||
/// \brief Erase the preamble file.
|
||||
void CleanPreambleFile();
|
||||
|
||||
@ -163,12 +156,6 @@ static const std::string &getPreambleFile(const ASTUnit *AU) {
|
||||
return getOnDiskData(AU).PreambleFile;
|
||||
}
|
||||
|
||||
void OnDiskData::CleanTemporaryFiles() {
|
||||
for (StringRef File : TemporaryFiles)
|
||||
llvm::sys::fs::remove(File);
|
||||
TemporaryFiles.clear();
|
||||
}
|
||||
|
||||
void OnDiskData::CleanPreambleFile() {
|
||||
if (!PreambleFile.empty()) {
|
||||
llvm::sys::fs::remove(PreambleFile);
|
||||
@ -177,7 +164,6 @@ void OnDiskData::CleanPreambleFile() {
|
||||
}
|
||||
|
||||
void OnDiskData::Cleanup() {
|
||||
CleanTemporaryFiles();
|
||||
CleanPreambleFile();
|
||||
}
|
||||
|
||||
@ -194,14 +180,6 @@ void ASTUnit::clearFileLevelDecls() {
|
||||
llvm::DeleteContainerSeconds(FileDecls);
|
||||
}
|
||||
|
||||
void ASTUnit::CleanTemporaryFiles() {
|
||||
getOnDiskData(this).CleanTemporaryFiles();
|
||||
}
|
||||
|
||||
void ASTUnit::addTemporaryFile(StringRef TempFile) {
|
||||
getOnDiskData(this).TemporaryFiles.push_back(TempFile);
|
||||
}
|
||||
|
||||
/// \brief After failing to build a precompiled preamble (due to
|
||||
/// errors in the source that occurs in the preamble), the number of
|
||||
/// reparses during which we'll skip even trying to precompile the
|
||||
@ -1100,7 +1078,6 @@ bool ASTUnit::Parse(std::shared_ptr<PCHContainerOperations> PCHContainerOps,
|
||||
// Clear out old caches and data.
|
||||
TopLevelDecls.clear();
|
||||
clearFileLevelDecls();
|
||||
CleanTemporaryFiles();
|
||||
|
||||
if (!OverrideMainBuffer) {
|
||||
checkAndRemoveNonDriverDiags(StoredDiagnostics);
|
||||
|
@ -2133,7 +2133,7 @@ void _mm_sfence(void);
|
||||
/// \headerfile <x86intrin.h>
|
||||
///
|
||||
/// \code
|
||||
/// void _mm_extract_pi(__m64 a, int n);
|
||||
/// int _mm_extract_pi16(__m64 a, int n);
|
||||
/// \endcode
|
||||
///
|
||||
/// This intrinsic corresponds to the <c> VPEXTRW / PEXTRW </c> instruction.
|
||||
@ -2157,7 +2157,7 @@ void _mm_sfence(void);
|
||||
/// \headerfile <x86intrin.h>
|
||||
///
|
||||
/// \code
|
||||
/// void _mm_insert_pi(__m64 a, int d, int n);
|
||||
/// __m64 _mm_insert_pi16(__m64 a, int d, int n);
|
||||
/// \endcode
|
||||
///
|
||||
/// This intrinsic corresponds to the <c> VPINSRW / PINSRW </c> instruction.
|
||||
@ -2680,8 +2680,7 @@ _mm_movelh_ps(__m128 __a, __m128 __b)
|
||||
///
|
||||
/// \headerfile <x86intrin.h>
|
||||
///
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + \c COMPOSITE </c>
|
||||
/// instruction.
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + COMPOSITE </c> instruction.
|
||||
///
|
||||
/// \param __a
|
||||
/// A 64-bit vector of [4 x i16]. The elements of the destination are copied
|
||||
@ -2711,8 +2710,7 @@ _mm_cvtpi16_ps(__m64 __a)
|
||||
///
|
||||
/// \headerfile <x86intrin.h>
|
||||
///
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + \c COMPOSITE </c>
|
||||
/// instruction.
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + COMPOSITE </c> instruction.
|
||||
///
|
||||
/// \param __a
|
||||
/// A 64-bit vector of 16-bit unsigned integer values. The elements of the
|
||||
@ -2741,8 +2739,7 @@ _mm_cvtpu16_ps(__m64 __a)
|
||||
///
|
||||
/// \headerfile <x86intrin.h>
|
||||
///
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + \c COMPOSITE </c>
|
||||
/// instruction.
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + COMPOSITE </c> instruction.
|
||||
///
|
||||
/// \param __a
|
||||
/// A 64-bit vector of [8 x i8]. The elements of the destination are copied
|
||||
@ -2766,8 +2763,7 @@ _mm_cvtpi8_ps(__m64 __a)
|
||||
///
|
||||
/// \headerfile <x86intrin.h>
|
||||
///
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + \c COMPOSITE </c>
|
||||
/// instruction.
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + COMPOSITE </c> instruction.
|
||||
///
|
||||
/// \param __a
|
||||
/// A 64-bit vector of unsigned 8-bit integer values. The elements of the
|
||||
@ -2791,8 +2787,7 @@ _mm_cvtpu8_ps(__m64 __a)
|
||||
///
|
||||
/// \headerfile <x86intrin.h>
|
||||
///
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + \c COMPOSITE </c>
|
||||
/// instruction.
|
||||
/// This intrinsic corresponds to the <c> CVTPI2PS + COMPOSITE </c> instruction.
|
||||
///
|
||||
/// \param __a
|
||||
/// A 64-bit vector of [2 x i32]. The lower elements of the destination are
|
||||
@ -2826,8 +2821,7 @@ _mm_cvtpi32x2_ps(__m64 __a, __m64 __b)
|
||||
///
|
||||
/// \headerfile <x86intrin.h>
|
||||
///
|
||||
/// This intrinsic corresponds to the <c> CVTPS2PI + \c COMPOSITE </c>
|
||||
/// instruction.
|
||||
/// This intrinsic corresponds to the <c> CVTPS2PI + COMPOSITE </c> instruction.
|
||||
///
|
||||
/// \param __a
|
||||
/// A 128-bit floating-point vector of [4 x float].
|
||||
@ -2857,8 +2851,7 @@ _mm_cvtps_pi16(__m128 __a)
|
||||
///
|
||||
/// \headerfile <x86intrin.h>
|
||||
///
|
||||
/// This intrinsic corresponds to the <c> CVTPS2PI + \c COMPOSITE </c>
|
||||
/// instruction.
|
||||
/// This intrinsic corresponds to the <c> CVTPS2PI + COMPOSITE </c> instruction.
|
||||
///
|
||||
/// \param __a
|
||||
/// 128-bit floating-point vector of [4 x float].
|
||||
|
@ -43,6 +43,8 @@ using namespace clang;
|
||||
|
||||
/// isObjCAtKeyword - Return true if we have an ObjC keyword identifier.
|
||||
bool Token::isObjCAtKeyword(tok::ObjCKeywordKind objcKey) const {
|
||||
if (isAnnotation())
|
||||
return false;
|
||||
if (IdentifierInfo *II = getIdentifierInfo())
|
||||
return II->getObjCKeywordID() == objcKey;
|
||||
return false;
|
||||
@ -50,6 +52,8 @@ bool Token::isObjCAtKeyword(tok::ObjCKeywordKind objcKey) const {
|
||||
|
||||
/// getObjCKeywordID - Return the ObjC keyword kind.
|
||||
tok::ObjCKeywordKind Token::getObjCKeywordID() const {
|
||||
if (isAnnotation())
|
||||
return tok::objc_not_keyword;
|
||||
IdentifierInfo *specId = getIdentifierInfo();
|
||||
return specId ? specId->getObjCKeywordID() : tok::objc_not_keyword;
|
||||
}
|
||||
|
@ -93,11 +93,10 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
|
||||
ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr),
|
||||
ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr),
|
||||
DictionaryWithObjectsMethod(nullptr), GlobalNewDeleteDeclared(false),
|
||||
TUKind(TUKind), NumSFINAEErrors(0), CachedFakeTopLevelModule(nullptr),
|
||||
AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
|
||||
NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
|
||||
CurrentInstantiationScope(nullptr), DisableTypoCorrection(false),
|
||||
TyposCorrected(0), AnalysisWarnings(*this),
|
||||
TUKind(TUKind), NumSFINAEErrors(0), AccessCheckingSFINAE(false),
|
||||
InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
|
||||
ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr),
|
||||
DisableTypoCorrection(false), TyposCorrected(0), AnalysisWarnings(*this),
|
||||
ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr),
|
||||
CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) {
|
||||
TUScope = nullptr;
|
||||
|
@ -16047,6 +16047,14 @@ void Sema::ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod) {
|
||||
ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules);
|
||||
|
||||
VisibleModules.setVisible(Mod, DirectiveLoc);
|
||||
|
||||
// The enclosing context is now part of this module.
|
||||
// FIXME: Consider creating a child DeclContext to hold the entities
|
||||
// lexically within the module.
|
||||
if (getLangOpts().trackLocalOwningModule()) {
|
||||
cast<Decl>(CurContext)->setHidden(true);
|
||||
cast<Decl>(CurContext)->setLocalOwningModule(Mod);
|
||||
}
|
||||
}
|
||||
|
||||
void Sema::ActOnModuleEnd(SourceLocation EomLoc, Module *Mod) {
|
||||
@ -16075,6 +16083,13 @@ void Sema::ActOnModuleEnd(SourceLocation EomLoc, Module *Mod) {
|
||||
DirectiveLoc = EomLoc;
|
||||
}
|
||||
BuildModuleInclude(DirectiveLoc, Mod);
|
||||
|
||||
// Any further declarations are in whatever module we returned to.
|
||||
if (getLangOpts().trackLocalOwningModule()) {
|
||||
cast<Decl>(CurContext)->setLocalOwningModule(getCurrentModule());
|
||||
if (!getCurrentModule())
|
||||
cast<Decl>(CurContext)->setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Sema::createImplicitModuleImportForErrorRecovery(SourceLocation Loc,
|
||||
|
@ -1326,62 +1326,6 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
|
||||
return !R.empty();
|
||||
}
|
||||
|
||||
Module *Sema::getOwningModule(Decl *Entity) {
|
||||
// If it's imported, grab its owning module.
|
||||
Module *M = Entity->getImportedOwningModule();
|
||||
if (M || !isa<NamedDecl>(Entity) || !cast<NamedDecl>(Entity)->isHidden())
|
||||
return M;
|
||||
assert(!Entity->isFromASTFile() &&
|
||||
"hidden entity from AST file has no owning module");
|
||||
|
||||
if (!getLangOpts().ModulesLocalVisibility) {
|
||||
// If we're not tracking visibility locally, the only way a declaration
|
||||
// can be hidden and local is if it's hidden because it's parent is (for
|
||||
// instance, maybe this is a lazily-declared special member of an imported
|
||||
// class).
|
||||
auto *Parent = cast<NamedDecl>(Entity->getDeclContext());
|
||||
assert(Parent->isHidden() && "unexpectedly hidden decl");
|
||||
return getOwningModule(Parent);
|
||||
}
|
||||
|
||||
// It's local and hidden; grab or compute its owning module.
|
||||
M = Entity->getLocalOwningModule();
|
||||
if (M)
|
||||
return M;
|
||||
|
||||
if (auto *Containing =
|
||||
PP.getModuleContainingLocation(Entity->getLocation())) {
|
||||
M = Containing;
|
||||
} else if (Entity->isInvalidDecl() || Entity->getLocation().isInvalid()) {
|
||||
// Don't bother tracking visibility for invalid declarations with broken
|
||||
// locations.
|
||||
cast<NamedDecl>(Entity)->setHidden(false);
|
||||
} else {
|
||||
// We need to assign a module to an entity that exists outside of any
|
||||
// module, so that we can hide it from modules that we textually enter.
|
||||
// Invent a fake module for all such entities.
|
||||
if (!CachedFakeTopLevelModule) {
|
||||
CachedFakeTopLevelModule =
|
||||
PP.getHeaderSearchInfo().getModuleMap().findOrCreateModule(
|
||||
"<top-level>", nullptr, false, false).first;
|
||||
|
||||
auto &SrcMgr = PP.getSourceManager();
|
||||
SourceLocation StartLoc =
|
||||
SrcMgr.getLocForStartOfFile(SrcMgr.getMainFileID());
|
||||
auto &TopLevel = ModuleScopes.empty()
|
||||
? VisibleModules
|
||||
: ModuleScopes[0].OuterVisibleModules;
|
||||
TopLevel.setVisible(CachedFakeTopLevelModule, StartLoc);
|
||||
}
|
||||
|
||||
M = CachedFakeTopLevelModule;
|
||||
}
|
||||
|
||||
if (M)
|
||||
Entity->setLocalOwningModule(M);
|
||||
return M;
|
||||
}
|
||||
|
||||
void Sema::makeMergedDefinitionVisible(NamedDecl *ND) {
|
||||
if (auto *M = getCurrentModule())
|
||||
Context.mergeDefinitionIntoModule(ND, M);
|
||||
@ -1520,7 +1464,6 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) {
|
||||
if (SemaRef.getLangOpts().ModulesLocalVisibility) {
|
||||
DeclModule = SemaRef.getOwningModule(D);
|
||||
if (!DeclModule) {
|
||||
// getOwningModule() may have decided the declaration should not be hidden.
|
||||
assert(!D->isHidden() && "hidden decl not from a module");
|
||||
return true;
|
||||
}
|
||||
|
@ -9348,12 +9348,6 @@ void ASTReader::diagnoseOdrViolations() {
|
||||
return Hash.CalculateHash();
|
||||
};
|
||||
|
||||
auto ComputeDeclNameODRHash = [&Hash](const DeclarationName Name) {
|
||||
Hash.clear();
|
||||
Hash.AddDeclarationName(Name);
|
||||
return Hash.CalculateHash();
|
||||
};
|
||||
|
||||
auto ComputeQualTypeODRHash = [&Hash](QualType Ty) {
|
||||
Hash.clear();
|
||||
Hash.AddQualType(Ty);
|
||||
@ -9446,11 +9440,8 @@ void ASTReader::diagnoseOdrViolations() {
|
||||
|
||||
QualType FirstType = FirstField->getType();
|
||||
QualType SecondType = SecondField->getType();
|
||||
const TypedefType *FirstTypedef = dyn_cast<TypedefType>(FirstType);
|
||||
const TypedefType *SecondTypedef = dyn_cast<TypedefType>(SecondType);
|
||||
|
||||
if ((FirstTypedef && !SecondTypedef) ||
|
||||
(!FirstTypedef && SecondTypedef)) {
|
||||
if (ComputeQualTypeODRHash(FirstType) !=
|
||||
ComputeQualTypeODRHash(SecondType)) {
|
||||
ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(),
|
||||
FieldTypeName)
|
||||
<< FirstII << FirstType;
|
||||
@ -9462,24 +9453,6 @@ void ASTReader::diagnoseOdrViolations() {
|
||||
break;
|
||||
}
|
||||
|
||||
if (FirstTypedef && SecondTypedef) {
|
||||
unsigned FirstHash = ComputeDeclNameODRHash(
|
||||
FirstTypedef->getDecl()->getDeclName());
|
||||
unsigned SecondHash = ComputeDeclNameODRHash(
|
||||
SecondTypedef->getDecl()->getDeclName());
|
||||
if (FirstHash != SecondHash) {
|
||||
ODRDiagError(FirstField->getLocation(),
|
||||
FirstField->getSourceRange(), FieldTypeName)
|
||||
<< FirstII << FirstType;
|
||||
ODRDiagNote(SecondField->getLocation(),
|
||||
SecondField->getSourceRange(), FieldTypeName)
|
||||
<< SecondII << SecondType;
|
||||
|
||||
Diagnosed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const bool IsFirstBitField = FirstField->isBitField();
|
||||
const bool IsSecondBitField = SecondField->isBitField();
|
||||
if (IsFirstBitField != IsSecondBitField) {
|
||||
|
@ -60,6 +60,10 @@ void B::func_fwd() {
|
||||
anonymous = 0;
|
||||
}
|
||||
|
||||
namespace C {
|
||||
void c();
|
||||
}
|
||||
void C::c() {}
|
||||
|
||||
// This should work even if 'i' and 'func' were declarations & not definitions,
|
||||
// but it doesn't yet.
|
||||
@ -114,6 +118,8 @@ void B::func_fwd() {
|
||||
// CHECK: [[M16]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[FUNC]], entity: [[FUNC_FWD:![0-9]+]]
|
||||
// CHECK: [[FUNC_FWD]] = distinct !DISubprogram(name: "func_fwd",{{.*}} line: 53,{{.*}} isDefinition: true
|
||||
// CHECK: [[M17]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[CTXT]], entity: [[I]]
|
||||
// CHECK: distinct !DISubprogram(name: "c",{{.*}}, scope: ![[C:[0-9]+]],{{.*}}, line: 60,{{.*}} isDefinition: true
|
||||
// CHECK: ![[C]] = !DINamespace(name: "C",
|
||||
|
||||
// CHECK-GMLT: [[CU:![0-9]+]] = distinct !DICompileUnit(
|
||||
// CHECK-GMLT-SAME: emissionKind: LineTablesOnly,
|
||||
|
@ -441,3 +441,7 @@ module DebugNestedB {
|
||||
header "DebugNestedB.h"
|
||||
export *
|
||||
}
|
||||
|
||||
module objcAtKeywordMissingEnd {
|
||||
header "objcAtKeywordMissingEnd.h"
|
||||
}
|
||||
|
3
test/Modules/Inputs/objcAtKeywordMissingEnd.h
Normal file
3
test/Modules/Inputs/objcAtKeywordMissingEnd.h
Normal file
@ -0,0 +1,3 @@
|
||||
@interface MissingEnd // expected-note {{class started here}}
|
||||
|
||||
@ // expected-error {{expected an Objective-C directive after '@'}} expected-error {{missing '@end'}}
|
@ -4,7 +4,11 @@ int m = n;
|
||||
#include "c.h"
|
||||
|
||||
#if defined(A) && !defined(ALLOW_NAME_LEAKAGE)
|
||||
#error A is defined
|
||||
#warning A is defined
|
||||
#endif
|
||||
|
||||
#define B
|
||||
|
||||
template<typename T> void b_template() {
|
||||
N::C::f(0);
|
||||
}
|
||||
|
@ -1 +1,10 @@
|
||||
#include "c.h"
|
||||
|
||||
#ifndef OTHER_H
|
||||
#define OTHER_H
|
||||
namespace N {
|
||||
struct C {
|
||||
template<typename U> static void f(U) {}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
7
test/Modules/objc-at-keyword.m
Normal file
7
test/Modules/objc-at-keyword.m
Normal file
@ -0,0 +1,7 @@
|
||||
// RUN: rm -rf %t
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -verify -x objective-c -fmodule-name=objcAtKeywordMissingEnd -emit-module %S/Inputs/module.map
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x objective-c -fmodule-name=Empty -emit-module %S/Inputs/module.map
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -verify -I %S/Inputs %s
|
||||
|
||||
@interface X // expected-note {{class started here}}
|
||||
#pragma clang module import Empty // expected-error {{missing '@end'}}
|
@ -634,6 +634,237 @@ S3 s3;
|
||||
#endif
|
||||
} // namespace Using
|
||||
|
||||
namespace RecordType {
|
||||
#if defined(FIRST)
|
||||
struct B1 {};
|
||||
struct S1 {
|
||||
B1 x;
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
struct A1 {};
|
||||
struct S1 {
|
||||
A1 x;
|
||||
};
|
||||
#else
|
||||
S1 s1;
|
||||
// expected-error@first.h:* {{'RecordType::S1::x' from module 'FirstModule' is not present in definition of 'RecordType::S1' in module 'SecondModule'}}
|
||||
// expected-note@second.h:* {{declaration of 'x' does not match}}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace DependentType {
|
||||
#if defined(FIRST)
|
||||
template <class T>
|
||||
class S1 {
|
||||
typename T::typeA x;
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
template <class T>
|
||||
class S1 {
|
||||
typename T::typeB x;
|
||||
};
|
||||
#else
|
||||
template<class T>
|
||||
using U1 = S1<T>;
|
||||
// expected-error@first.h:* {{'DependentType::S1::x' from module 'FirstModule' is not present in definition of 'S1<T>' in module 'SecondModule'}}
|
||||
// expected-note@second.h:* {{declaration of 'x' does not match}}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace ElaboratedType {
|
||||
#if defined(FIRST)
|
||||
namespace N1 { using type = double; }
|
||||
struct S1 {
|
||||
N1::type x;
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
namespace N1 { using type = int; }
|
||||
struct S1 {
|
||||
N1::type x;
|
||||
};
|
||||
#else
|
||||
S1 s1;
|
||||
// expected-error@first.h:* {{'ElaboratedType::S1::x' from module 'FirstModule' is not present in definition of 'ElaboratedType::S1' in module 'SecondModule'}}
|
||||
// expected-note@second.h:* {{declaration of 'x' does not match}}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace Enum {
|
||||
#if defined(FIRST)
|
||||
enum A1 {};
|
||||
struct S1 {
|
||||
A1 x;
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
enum A2 {};
|
||||
struct S1 {
|
||||
A2 x;
|
||||
};
|
||||
#else
|
||||
S1 s1;
|
||||
// expected-error@first.h:* {{'Enum::S1::x' from module 'FirstModule' is not present in definition of 'Enum::S1' in module 'SecondModule'}}
|
||||
// expected-note@second.h:* {{declaration of 'x' does not match}}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace NestedNamespaceSpecifier {
|
||||
#if defined(FIRST)
|
||||
namespace LevelA1 {
|
||||
using Type = int;
|
||||
}
|
||||
|
||||
struct S1 {
|
||||
LevelA1::Type x;
|
||||
};
|
||||
# elif defined(SECOND)
|
||||
namespace LevelB1 {
|
||||
namespace LevelC1 {
|
||||
using Type = int;
|
||||
}
|
||||
}
|
||||
|
||||
struct S1 {
|
||||
LevelB1::LevelC1::Type x;
|
||||
};
|
||||
#else
|
||||
S1 s1;
|
||||
// expected-error@second.h:* {{'NestedNamespaceSpecifier::S1' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with type 'LevelB1::LevelC1::Type' (aka 'int')}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with type 'LevelA1::Type' (aka 'int')}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
namespace LevelA2 { using Type = int; }
|
||||
struct S2 {
|
||||
LevelA2::Type x;
|
||||
};
|
||||
# elif defined(SECOND)
|
||||
struct S2 {
|
||||
int x;
|
||||
};
|
||||
#else
|
||||
S2 s2;
|
||||
// expected-error@second.h:* {{'NestedNamespaceSpecifier::S2' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with type 'int'}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with type 'LevelA2::Type' (aka 'int')}}
|
||||
#endif
|
||||
|
||||
namespace LevelA3 { using Type = int; }
|
||||
namespace LevelB3 { using Type = int; }
|
||||
#if defined(FIRST)
|
||||
struct S3 {
|
||||
LevelA3::Type x;
|
||||
};
|
||||
# elif defined(SECOND)
|
||||
struct S3 {
|
||||
LevelB3::Type x;
|
||||
};
|
||||
#else
|
||||
S3 s3;
|
||||
// expected-error@second.h:* {{'NestedNamespaceSpecifier::S3' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with type 'LevelB3::Type' (aka 'int')}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with type 'LevelA3::Type' (aka 'int')}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct TA4 { using Type = int; };
|
||||
struct S4 {
|
||||
TA4::Type x;
|
||||
};
|
||||
# elif defined(SECOND)
|
||||
struct TB4 { using Type = int; };
|
||||
struct S4 {
|
||||
TB4::Type x;
|
||||
};
|
||||
#else
|
||||
S4 s4;
|
||||
// expected-error@second.h:* {{'NestedNamespaceSpecifier::S4' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with type 'TB4::Type' (aka 'int')}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with type 'TA4::Type' (aka 'int')}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
struct T5 { using Type = int; };
|
||||
struct S5 {
|
||||
T5::Type x;
|
||||
};
|
||||
# elif defined(SECOND)
|
||||
namespace T5 { using Type = int; };
|
||||
struct S5 {
|
||||
T5::Type x;
|
||||
};
|
||||
#else
|
||||
S5 s5;
|
||||
// expected-error@second.h:* {{'NestedNamespaceSpecifier::S5' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with type 'T5::Type' (aka 'int')}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with type 'T5::Type' (aka 'int')}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
namespace N6 {using I = int;}
|
||||
struct S6 {
|
||||
NestedNamespaceSpecifier::N6::I x;
|
||||
};
|
||||
# elif defined(SECOND)
|
||||
using I = int;
|
||||
struct S6 {
|
||||
::NestedNamespaceSpecifier::I x;
|
||||
};
|
||||
#else
|
||||
S6 s6;
|
||||
// expected-error@second.h:* {{'NestedNamespaceSpecifier::S6' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with type '::NestedNamespaceSpecifier::I' (aka 'int')}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with type 'NestedNamespaceSpecifier::N6::I' (aka 'int')}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
template <class T, class U>
|
||||
class S7 {
|
||||
typename T::type *x = {};
|
||||
int z = x->T::foo();
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
template <class T, class U>
|
||||
class S7 {
|
||||
typename T::type *x = {};
|
||||
int z = x->U::foo();
|
||||
};
|
||||
#else
|
||||
template <class T, class U>
|
||||
using U7 = S7<T, U>;
|
||||
// expected-error@second.h:* {{'NestedNamespaceSpecifier::S7' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'z' with an initializer}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found field 'z' with a different initializer}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
template <class T>
|
||||
class S8 {
|
||||
int x = T::template X<int>::value;
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
template <class T>
|
||||
class S8 {
|
||||
int x = T::template Y<int>::value;
|
||||
};
|
||||
#else
|
||||
template <class T>
|
||||
using U8 = S8<T>;
|
||||
// expected-error@second.h:* {{'NestedNamespaceSpecifier::S8' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with an initializer}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with a different initializer}}
|
||||
#endif
|
||||
|
||||
#if defined(FIRST)
|
||||
namespace N9 { using I = int; }
|
||||
namespace O9 = N9;
|
||||
struct S9 {
|
||||
O9::I x;
|
||||
};
|
||||
#elif defined(SECOND)
|
||||
namespace N9 { using I = int; }
|
||||
namespace P9 = N9;
|
||||
struct S9 {
|
||||
P9::I x;
|
||||
};
|
||||
#else
|
||||
S9 s9;
|
||||
// expected-error@second.h:* {{'NestedNamespaceSpecifier::S9' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with type 'P9::I' (aka 'int')}}
|
||||
// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with type 'O9::I' (aka 'int')}}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Interesting cases that should not cause errors. struct S should not error
|
||||
// while struct T should error at the access specifier mismatch at the end.
|
||||
|
@ -3,6 +3,11 @@
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-local-submodule-visibility -fmodules-cache-path=%t -I%S/Inputs/submodule-visibility -verify %s -DIMPORT
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-local-submodule-visibility -fmodules-cache-path=%t -fmodule-name=x -I%S/Inputs/submodule-visibility -verify %s
|
||||
// RUN: %clang_cc1 -fimplicit-module-maps -fmodules-local-submodule-visibility -fmodules-cache-path=%t -I%S/Inputs/submodule-visibility -verify %s
|
||||
//
|
||||
// Explicit module builds.
|
||||
// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -emit-module -x c++-module-map %S/Inputs/submodule-visibility/module.modulemap -fmodule-name=other -o %t/other.pcm
|
||||
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%S/Inputs/submodule-visibility/module.modulemap -fmodules-local-submodule-visibility -fmodule-file=%t/other.pcm -verify -fmodule-name=x -I%S/Inputs/submodule-visibility %s
|
||||
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%S/Inputs/submodule-visibility/module.modulemap -fmodule-file=%t/other.pcm -verify -fmodule-name=x -I%S/Inputs/submodule-visibility %s -DALLOW_TEXTUAL_NAME_LEAKAGE
|
||||
|
||||
#include "a.h"
|
||||
#include "b.h"
|
||||
@ -11,6 +16,8 @@
|
||||
// expected-no-diagnostics
|
||||
#elif IMPORT
|
||||
// expected-error@-6 {{could not build module 'x'}}
|
||||
#elif ALLOW_TEXTUAL_NAME_LEAKAGE
|
||||
// expected-warning@b.h:7 {{A is defined}}
|
||||
#else
|
||||
// The use of -fmodule-name=x causes us to textually include the above headers.
|
||||
// The submodule visibility rules are still applied in this case.
|
||||
@ -35,3 +42,5 @@ typedef struct {
|
||||
int p;
|
||||
void (*f)(int p);
|
||||
} name_for_linkage;
|
||||
|
||||
void g() { b_template<int>(); }
|
||||
|
Loading…
Reference in New Issue
Block a user