Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1854,9 +1854,15 @@ class ASTContext : public RefCountedBase<ASTContext> {

private:
/// Return a normal function type with a typed argument list.
QualType getFunctionTypeInternal(QualType ResultTy, ArrayRef<QualType> Args,
const FunctionProtoType::ExtProtoInfo &EPI,
bool OnlyWantCanonical) const;
QualType
getFunctionTypeInternal(QualType ResultTy, ArrayRef<QualType> Args,
const FunctionProtoType::ExtProtoInfo &EPI,
bool OnlyWantCanonical,
bool IgnoringUnresolvedLookupExpr = false) const;

QualType getFunctionTypeWithExceptionSpecInternal(
QualType Orig, const FunctionProtoType::ExceptionSpecInfo &ESI,
bool IgnoringUnresolvedLookupExpr) const;

public:
QualType getTypeDeclType(ElaboratedTypeKeyword Keyword,
Expand Down
27 changes: 26 additions & 1 deletion clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1617,8 +1617,33 @@ class alignas(void *) Stmt {
/// other lambda expressions. When true, the lambda expressions with the same
/// implementation will be considered to be the same. ProfileLambdaExpr should
/// only be true when we try to merge two declarations within modules.
/// \param IgnoringUnresolvedLookupExpr whether or not to ignore
/// UnresolvedLookupExpr when profiling. When true,
/// IgnoringUnresolvedLookupExpr won't be invoked during profiling. This is
/// useful in case we don't hope the unresolved lookup expr to pollute the
/// profile result. e.g.,
///
/// "a.h"
///
/// #pragma once
/// struct F {
/// template <typename... T> requires ((sizeof(T) > 0) && ...)
/// void operator()(T...) {}
/// } f;
///
/// and
///
/// "c.h"
///
/// void operator&&(struct X, struct X);
/// #include "a.h"
///
/// Here the `F::operator()` may produce different profiling results depending
/// on whether there is a freestanding `operator&&` declared before it. And
/// this affects declaration merging in modules.
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
bool Canonical, bool ProfileLambdaExpr = false) const;
bool Canonical, bool ProfileLambdaExpr = false,
bool IgnoringUnresolvedLookupExpr = false) const;

/// Calculate a unique representation for a statement that is
/// stable across compiler invocations.
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/AST/TypeBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -5953,7 +5953,8 @@ class FunctionProtoType final
static void Profile(llvm::FoldingSetNodeID &ID, QualType Result,
param_type_iterator ArgTys, unsigned NumArgs,
const ExtProtoInfo &EPI, const ASTContext &Context,
bool Canonical);
bool Canonical,
bool IgnoringUnresolvedLookupExpr = false);
};

/// The elaboration keyword that precedes a qualified type name or
Expand Down
38 changes: 26 additions & 12 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3794,21 +3794,32 @@ void ASTContext::adjustDeducedFunctionResultType(FunctionDecl *FD,
/// specified exception specification. Type sugar that can be present on a
/// declaration of a function with an exception specification is permitted
/// and preserved. Other type sugar (for instance, typedefs) is not.
QualType ASTContext::getFunctionTypeWithExceptionSpec(
QualType Orig, const FunctionProtoType::ExceptionSpecInfo &ESI) const {
QualType ASTContext::getFunctionTypeWithExceptionSpecInternal(
QualType Orig, const FunctionProtoType::ExceptionSpecInfo &ESI,
bool IgnoringUnresolvedLookupExpr) const {
return adjustType(Orig, [&](QualType Ty) {
const auto *Proto = Ty->castAs<FunctionProtoType>();
return getFunctionType(Proto->getReturnType(), Proto->getParamTypes(),
Proto->getExtProtoInfo().withExceptionSpec(ESI));
return getFunctionTypeInternal(
Proto->getReturnType(), Proto->getParamTypes(),
Proto->getExtProtoInfo().withExceptionSpec(ESI),
/*OnlyWantCanonical=*/false, IgnoringUnresolvedLookupExpr);
});
}

QualType ASTContext::getFunctionTypeWithExceptionSpec(
QualType Orig, const FunctionProtoType::ExceptionSpecInfo &ESI) const {
return getFunctionTypeWithExceptionSpecInternal(
Orig, ESI, /*IgnoringUnresolvedLookupExpr=*/false);
}

bool ASTContext::hasSameFunctionTypeIgnoringExceptionSpec(QualType T,
QualType U) const {
return hasSameType(T, U) ||
(getLangOpts().CPlusPlus17 &&
hasSameType(getFunctionTypeWithExceptionSpec(T, EST_None),
getFunctionTypeWithExceptionSpec(U, EST_None)));
hasSameType(getFunctionTypeWithExceptionSpecInternal(
T, EST_None, /*IgnoringUnresolvedLookupExpr=*/true),
getFunctionTypeWithExceptionSpecInternal(
U, EST_None, /*IgnoringUnresolvedLookupExpr=*/true)));
}

QualType ASTContext::getFunctionTypeWithoutPtrSizes(QualType T) {
Expand Down Expand Up @@ -4988,14 +4999,15 @@ static bool isCanonicalExceptionSpecification(

QualType ASTContext::getFunctionTypeInternal(
QualType ResultTy, ArrayRef<QualType> ArgArray,
const FunctionProtoType::ExtProtoInfo &EPI, bool OnlyWantCanonical) const {
const FunctionProtoType::ExtProtoInfo &EPI, bool OnlyWantCanonical,
bool IgnoringUnresolvedLookupExpr) const {
size_t NumArgs = ArgArray.size();

// Unique functions, to guarantee there is only one function of a particular
// structure.
llvm::FoldingSetNodeID ID;
FunctionProtoType::Profile(ID, ResultTy, ArgArray.begin(), NumArgs, EPI,
*this, true);
*this, true, IgnoringUnresolvedLookupExpr);

QualType Canonical;
bool Unique = false;
Expand Down Expand Up @@ -7454,8 +7466,12 @@ bool ASTContext::isSameConstraintExpr(const Expr *XCE, const Expr *YCE) const {
return true;

llvm::FoldingSetNodeID XCEID, YCEID;
XCE->Profile(XCEID, *this, /*Canonical=*/true, /*ProfileLambdaExpr=*/true);
YCE->Profile(YCEID, *this, /*Canonical=*/true, /*ProfileLambdaExpr=*/true);
/// The unresolved lookup expr may misguide the profiling results. See
/// clang/test/Modules/callable-require-clause-merge.cppm for an example.
XCE->Profile(XCEID, *this, /*Canonical=*/true, /*ProfileLambdaExpr=*/true,
/*IgnoringUnresolvedLookupExpr=*/true);
YCE->Profile(YCEID, *this, /*Canonical=*/true, /*ProfileLambdaExpr=*/true,
/*IgnoringUnresolvedLookupExpr=*/true);
return XCEID == YCEID;
}

Expand Down Expand Up @@ -7779,8 +7795,6 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
auto *XFPT = XT->getAs<FunctionProtoType>();
auto *YFPT = YT->getAs<FunctionProtoType>();
if (getLangOpts().CPlusPlus17 && XFPT && YFPT &&
(isUnresolvedExceptionSpec(XFPT->getExceptionSpecType()) ||
isUnresolvedExceptionSpec(YFPT->getExceptionSpecType())) &&
hasSameFunctionTypeIgnoringExceptionSpec(XT, YT))
return true;
return false;
Expand Down
28 changes: 21 additions & 7 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ namespace {
llvm::FoldingSetNodeID &ID;
bool Canonical;
bool ProfileLambdaExpr;
bool IgnoringUnresolvedLookupExpr;

public:
StmtProfiler(llvm::FoldingSetNodeID &ID, bool Canonical,
bool ProfileLambdaExpr)
: ID(ID), Canonical(Canonical), ProfileLambdaExpr(ProfileLambdaExpr) {}
bool ProfileLambdaExpr, bool IgnoringUnresolvedLookupExpr)
: ID(ID), Canonical(Canonical), ProfileLambdaExpr(ProfileLambdaExpr),
IgnoringUnresolvedLookupExpr(IgnoringUnresolvedLookupExpr) {}

virtual ~StmtProfiler() {}

Expand Down Expand Up @@ -86,8 +88,11 @@ namespace {
public:
StmtProfilerWithPointers(llvm::FoldingSetNodeID &ID,
const ASTContext &Context, bool Canonical,
bool ProfileLambdaExpr)
: StmtProfiler(ID, Canonical, ProfileLambdaExpr), Context(Context) {}
bool ProfileLambdaExpr,
bool IgnoringUnresolvedLookupExpr)
: StmtProfiler(ID, Canonical, ProfileLambdaExpr,
IgnoringUnresolvedLookupExpr),
Context(Context) {}

private:
void HandleStmtClass(Stmt::StmtClass SC) override {
Expand Down Expand Up @@ -184,8 +189,11 @@ namespace {
class StmtProfilerWithoutPointers : public StmtProfiler {
ODRHash &Hash;
public:
// Set IgnoringUnresolvedLookupExpr as we don't want the unresolved lookup
// expr affecting the merging results.
StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
: StmtProfiler(ID, /*Canonical=*/false, /*ProfileLambdaExpr=*/false),
: StmtProfiler(ID, /*Canonical=*/false, /*ProfileLambdaExpr=*/false,
/*IgnoringUnresolvedLookupExpr=*/true),
Hash(Hash) {}

private:
Expand Down Expand Up @@ -2269,6 +2277,10 @@ void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) {

void
StmtProfiler::VisitUnresolvedLookupExpr(const UnresolvedLookupExpr *S) {
if (IgnoringUnresolvedLookupExpr) {
ID.AddInteger(0);
return;
}
VisitOverloadExpr(S);
}

Expand Down Expand Up @@ -2951,8 +2963,10 @@ void StmtProfiler::VisitHLSLOutArgExpr(const HLSLOutArgExpr *S) {
}

void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
bool Canonical, bool ProfileLambdaExpr) const {
StmtProfilerWithPointers Profiler(ID, Context, Canonical, ProfileLambdaExpr);
bool Canonical, bool ProfileLambdaExpr,
bool IgnoringUnresolvedLookupExpr) const {
StmtProfilerWithPointers Profiler(ID, Context, Canonical, ProfileLambdaExpr,
IgnoringUnresolvedLookupExpr);
Profiler.Visit(this);
}

Expand Down
12 changes: 10 additions & 2 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3996,7 +3996,8 @@ bool FunctionProtoType::isTemplateVariadic() const {
void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
const QualType *ArgTys, unsigned NumParams,
const ExtProtoInfo &epi,
const ASTContext &Context, bool Canonical) {
const ASTContext &Context, bool Canonical,
bool IgnoringUnresolvedLookupExpr) {
// We have to be careful not to get ambiguous profile encodings.
// Note that valid type pointers are never ambiguous with anything else.
//
Expand Down Expand Up @@ -4035,7 +4036,14 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
for (QualType Ex : epi.ExceptionSpec.Exceptions)
ID.AddPointer(Ex.getAsOpaquePtr());
} else if (isComputedNoexcept(epi.ExceptionSpec.Type)) {
epi.ExceptionSpec.NoexceptExpr->Profile(ID, Context, Canonical);
// It may be problematic if we don't ignore the unresolved lookup expr.
// See clang/test/Modules/polluted-operator.cppm for an example
// for it.
//
// ProfileLambdaExpr=false is the default value.
epi.ExceptionSpec.NoexceptExpr->Profile(ID, Context, Canonical,
/*ProfileLambdaExpr=*/false,
IgnoringUnresolvedLookupExpr);
} else if (epi.ExceptionSpec.Type == EST_Uninstantiated ||
epi.ExceptionSpec.Type == EST_Unevaluated) {
ID.AddPointer(epi.ExceptionSpec.SourceDecl->getCanonicalDecl());
Expand Down
43 changes: 43 additions & 0 deletions clang/test/Modules/callable-require-clause-merge.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: split-file %s %t
//
// RUN: %clang_cc1 -std=c++20 %t/mymod.cppm -emit-module-interface -o %t/mymod.pcm
// RUN: %clang_cc1 -std=c++20 %t/consumer.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
//
// RUN: %clang_cc1 -std=c++20 %t/mymod.cppm -emit-reduced-module-interface -o %t/mymod.pcm
// RUN: %clang_cc1 -std=c++20 %t/consumer.cpp -fprebuilt-module-path=%t -fsyntax-only -verify

// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/mymod.cppm -emit-module-interface -o %t/mymod.pcm
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/consumer.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
//
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/mymod.cppm -emit-reduced-module-interface -o %t/mymod.pcm
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/consumer.cpp -fprebuilt-module-path=%t -fsyntax-only -verify

//--- r.h
struct F {
template <typename... T> requires ((sizeof(T) > 0) && ...)
void operator()(T...) {}
} f;

struct G {
template <typename T, typename U>
requires requires(T t, U u) { t + u; }
void operator()(T, U) {}
} g;

//--- mymod.cppm
module;
#include "r.h"
export module mymod;
export using ::f;
export using ::g;

//--- consumer.cpp
// expected-no-diagnostics
void operator&&(struct X, struct X);
void operator+(struct X, struct Y);
#include "r.h"
import mymod;

void h() { f(); g(1, 2); }
5 changes: 0 additions & 5 deletions clang/test/Modules/polluted-operator.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,4 @@ export namespace std {
using std::operator&&;
}

#ifdef SKIP_ODR_CHECK_IN_GMF
// expected-no-diagnostics
#else
// expected-error@* {{has different definitions in different modules; first difference is defined here found data member '_S_copy_ctor' with an initializer}}
// expected-note@* {{but in 'a.<global>' found data member '_S_copy_ctor' with a different initializer}}
#endif