Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -771,6 +771,7 @@ struct ExtInfo : public QualifierInfo { TypeSourceInfo *TInfo; Expr *TrailingRequiresClause = nullptr; + Expr *OriginalTrailingRequiresClause = nullptr; }; llvm::PointerUnion DeclInfo; @@ -850,6 +851,18 @@ : nullptr; } + /// \brief The original constraint-expression introduced by the trailing + /// requires-clause. This can be used for ODR-Checking since the trailing + /// requires clause may change during the semantic analysis. + Expr *getOriginalTrailingRequiresClause() { + return hasExtInfo() ? getExtInfo()->OriginalTrailingRequiresClause + : nullptr; + } + const Expr *getOriginalTrailingRequiresClause() const { + return const_cast(this) + ->getOriginalTrailingRequiresClause(); + } + void setTrailingRequiresClause(Expr *TrailingRequiresClause); unsigned getNumTemplateParameterLists() const { Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -6683,8 +6683,8 @@ return false; } - if (!isSameConstraintExpr(FuncX->getTrailingRequiresClause(), - FuncY->getTrailingRequiresClause())) + if (!isSameConstraintExpr(FuncX->getOriginalTrailingRequiresClause(), + FuncY->getOriginalTrailingRequiresClause())) return false; auto GetTypeAsWritten = [](const FunctionDecl *FD) { Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -1954,6 +1954,8 @@ DeclInfo = new (getASTContext()) ExtInfo; // Restore savedTInfo into (extended) decl info. getExtInfo()->TInfo = savedTInfo; + // We should only store TrailingRequiresClause once. + getExtInfo()->OriginalTrailingRequiresClause = TrailingRequiresClause; } // Set requires clause info. getExtInfo()->TrailingRequiresClause = TrailingRequiresClause; Index: clang/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- clang/lib/Serialization/ASTReaderDecl.cpp +++ clang/lib/Serialization/ASTReaderDecl.cpp @@ -885,6 +885,7 @@ auto *Info = new (Reader.getContext()) DeclaratorDecl::ExtInfo(); Record.readQualifierInfo(*Info); Info->TrailingRequiresClause = Record.readExpr(); + Info->OriginalTrailingRequiresClause = Record.readExpr(); DD->DeclInfo = Info; } QualType TSIType = Record.readType(); Index: clang/lib/Serialization/ASTWriterDecl.cpp =================================================================== --- clang/lib/Serialization/ASTWriterDecl.cpp +++ clang/lib/Serialization/ASTWriterDecl.cpp @@ -551,6 +551,7 @@ DeclaratorDecl::ExtInfo *Info = D->getExtInfo(); Record.AddQualifierInfo(*Info); Record.AddStmt(Info->TrailingRequiresClause); + Record.AddStmt(Info->OriginalTrailingRequiresClause); } // The location information is deferred until the end of the record. Record.AddTypeRef(D->getTypeSourceInfo() ? D->getTypeSourceInfo()->getType() Index: clang/test/Modules/pr60890.cppm =================================================================== --- /dev/null +++ clang/test/Modules/pr60890.cppm @@ -0,0 +1,53 @@ +// https://github.com/llvm/llvm-project/issues/60890 +// +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/a.cppm -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/b.cppm -fprebuilt-module-path=%t -o %t/b.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/c.cppm -fprebuilt-module-path=%t -o %t/c.pcm +// RUN: %clang_cc1 -std=c++20 %t/d.cpp -fprebuilt-module-path=%t -fsyntax-only -verify + +//--- a.cppm +export module a; + +export template +struct a { + friend void aa(a x) requires(true) {} + void aaa() requires(true) {} +}; + +//--- b.cppm +export module b; + +import a; + +void b() { + a _; +} + +//--- c.cppm +export module c; + +import a; + +struct c { + void f() const { + a _; + aa(_); + _.aaa(); + } +}; + +//--- d.cpp +// expected-no-diagnostics +import a; +import b; +import c; + +void d() { + a _; + aa(_); + _.aaa(); +}