From 5bbadec2d1e0d5d0d25295759d49f6cd420d0968 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Thu, 6 Apr 2023 11:01:58 +0800 Subject: [PATCH] Recommit [C++20] [Modules] Don't load declaration eagerly for named modules Close https://github.com/llvm/llvm-project/issues/61064. The root cause of the issue is that we will deserilize some declarations eagerly when reading the BMI. However, many declarations in the BMI are not necessary for the importer. So it wastes a lot of time. The new commit handles the MSVC's extension #pragma comment and #pragma detect_mismatch to follow MSVC's behavior. See pr61783 for details. --- clang/lib/Serialization/ASTWriterDecl.cpp | 15 +++- clang/test/Modules/no-eager-load.cppm | 110 ++++++++++++++++++++++++++++++ clang/test/Modules/pr61783.cppm | 29 ++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 clang/test/Modules/no-eager-load.cppm create mode 100644 clang/test/Modules/pr61783.cppm diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index abb6bb7..54b5e38 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -2500,7 +2500,20 @@ void ASTWriter::WriteDeclAbbrevs() { /// relatively painless since they would presumably only do it for top-level /// decls. static bool isRequiredDecl(const Decl *D, ASTContext &Context, - bool WritingModule) { + Module *WritingModule) { + // Named modules have different semantics than header modules. Every named + // module units owns a translation unit. So the importer of named modules + // doesn't need to deserilize everything ahead of time. + if (WritingModule && WritingModule->isModulePurview()) { + // The PragmaCommentDecl and PragmaDetectMismatchDecl are MSVC's extension. + // And the behavior of MSVC for such cases will leak this to the module + // users. Given pragma is not a standard thing, the compiler has the space + // to do their own decision. Let's follow MSVC here. + if (isa(D)) + return true; + return false; + } + // An ObjCMethodDecl is never considered as "required" because its // implementation container always is. diff --git a/clang/test/Modules/no-eager-load.cppm b/clang/test/Modules/no-eager-load.cppm new file mode 100644 index 0000000..6632cc6 --- /dev/null +++ b/clang/test/Modules/no-eager-load.cppm @@ -0,0 +1,110 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %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 -o %t/b.pcm +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/c.cpp \ +// RUN: -fprebuilt-module-path=%t +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/d.cpp \ +// RUN: -fprebuilt-module-path=%t +// +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/e.cppm -o %t/e.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/f.cppm -o %t/f.pcm +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/g.cpp \ +// RUN: -fprebuilt-module-path=%t +// +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/h.cppm \ +// RUN: -fprebuilt-module-path=%t -o %t/h.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/i.cppm \ +// RUN: -fprebuilt-module-path=%t -o %t/i.pcm +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/j.cpp \ +// RUN: -fprebuilt-module-path=%t +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/k.cpp \ +// RUN: -fprebuilt-module-path=%t + +//--- a.cppm +export module a; +export void foo() { + +} + +//--- b.cppm +export module b; +void bar(); +export void foo() { + bar(); +} + +//--- c.cpp +// expected-no-diagnostics +// Since we will load all the declaration lazily, we won't be able to find +// the ODR violation here. +import a; +import b; + +//--- d.cpp +import a; +import b; +// Test that we can still check the odr violation if we call the function +// actually. +void use() { + foo(); // expected-error@* {{'foo' has different definitions in different modules;}} + // expected-note@* {{but in 'a' found a different body}} +} + +//--- foo.h +void foo() { + +} + +//--- bar.h +void bar(); +void foo() { + bar(); +} + +//--- e.cppm +module; +#include "foo.h" +export module e; +export using ::foo; + +//--- f.cppm +module; +#include "bar.h" +export module f; +export using ::foo; + +//--- g.cpp +import e; +import f; +void use() { + foo(); // expected-error@* {{'foo' has different definitions in different modules;}} + // expected-note@* {{but in 'e.' found a different body}} +} + +//--- h.cppm +export module h; +export import a; +export import b; + +//--- i.cppm +export module i; +export import e; +export import f; + +//--- j.cpp +import h; +void use() { + foo(); // expected-error@* {{'foo' has different definitions in different modules;}} + // expected-note@* {{but in 'a' found a different body}} +} + +//--- k.cpp +import i; +void use() { + foo(); // expected-error@* {{'foo' has different definitions in different modules;}} + // expected-note@* {{but in 'e.' found a different body}} +} + diff --git a/clang/test/Modules/pr61783.cppm b/clang/test/Modules/pr61783.cppm new file mode 100644 index 0000000..9cf773b --- /dev/null +++ b/clang/test/Modules/pr61783.cppm @@ -0,0 +1,29 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-windows-msvc19.11.0 -fms-extensions %t/mod.cppm -emit-module-interface \ +// RUN: -o %t/mod.pcm +// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-windows-msvc19.11.0 -fms-extensions %t/mod.pcm -S -emit-llvm -o - | \ +// RUN: FileCheck %t/mod.cppm +// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-windows-msvc19.11.0 -fms-extensions %t/user.cpp -fmodule-file=mod=%t/mod.pcm \ +// RUN: -S -emit-llvm -o - | FileCheck %t/user.cpp + +//--- mod.cppm +module; + +#pragma comment(lib, "msvcprt.lib") +#pragma detect_mismatch("myLib_version", "9") + +export module mod; + +// CHECK: ![[NUM:[0-9]+]] ={{.*}}msvcprt.lib +// CHECK: ![[NUM:[0-9]+]] ={{.*}}FAILIFMISMATCH{{.*}}myLib_version=9 + +//--- user.cpp +#pragma detect_mismatch("myLib_version", "1") +import mod; + +// CHECK: ![[NUM:[0-9]+]] ={{.*}}FAILIFMISMATCH{{.*}}myLib_version=1 +// CHECK: ![[NUM:[0-9]+]] ={{.*}}msvcprt.lib +// CHECK: ![[NUM:[0-9]+]] ={{.*}}FAILIFMISMATCH{{.*}}myLib_version=9 -- 2.7.4