
Summary: Because AST loading code is lazy and happens in unpredictable order, it is possible that a function and lambda inside the function can be loaded from different modules. As a result, the captured DeclRefExpr won’t match the corresponding VarDecl inside the function. This situation is reflected in the AST as follows: ``` FunctionDecl 0x555564f4aff0 <Conv.h:33:1, line:41:1> line:33:35 imported in ./thrift_cpp2_base.h hidden tryTo 'Expected<Tgt, const char *> ()' inline |-also in ./folly-conv.h `-CompoundStmt 0x555564f7cfc8 <col:43, line:41:1> |-DeclStmt 0x555564f7ced8 <line:34:3, col:17> | `-VarDecl 0x555564f7cef8 <col:3, col:16> col:7 imported in ./thrift_cpp2_base.h hidden referenced result 'Tgt' cinit | `-IntegerLiteral 0x555564f7d080 <col:16> 'int' 0 |-CallExpr 0x555564f7cea8 <line:39:3, col:76> '<dependent type>' | |-UnresolvedLookupExpr 0x555564f7bea0 <col:3, col:19> '<overloaded function type>' lvalue (no ADL) = 'then_' 0x555564f7bef0 | |-CXXTemporaryObjectExpr 0x555564f7bcb0 <col:25, col:45> 'Expected<bool, int>':'folly::Expected<bool, int>' 'void () noexcept' zeroing | `-LambdaExpr 0x555564f7bc88 <col:48, col:75> '(lambda at Conv.h:39:48)' | |-CXXRecordDecl 0x555564f76b88 <col:48> col:48 imported in ./folly-conv.h hidden implicit <undeserialized declarations> class definition | | |-also in ./thrift_cpp2_base.h | | `-DefinitionData lambda empty standard_layout trivially_copyable literal can_const_default_init | | |-DefaultConstructor defaulted_is_constexpr | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param | | |-MoveConstructor exists simple trivial needs_implicit | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param | | |-MoveAssignment | | `-Destructor simple irrelevant trivial constexpr needs_implicit | `-CompoundStmt 0x555564f7d1a8 <col:58, col:75> | `-ReturnStmt 0x555564f7d198 <col:60, col:67> | `-DeclRefExpr 0x555564f7d0a0 <col:67> 'Tgt' lvalue Var 0x555564f7d0c8 'result' 'Tgt' refers_to_enclosing_variable_or_capture `-ReturnStmt 0x555564f7bc78 <line:40:3, col:11> `-InitListExpr 0x555564f7bc38 <col:10, col:11> 'void' ``` This diff modifies the AST deserialization process to load lambdas within the canonical function declaration sooner, immediately following the function, ensuring that they are loaded from the same module. Re-land https://github.com/llvm/llvm-project/pull/104512 Added test case that caused crash due to multiple enclosed lambdas deserialization. Test Plan: check-clang
77 lines
2.2 KiB
C++
77 lines
2.2 KiB
C++
// RUN: rm -fR %t
|
|
// RUN: split-file %s %t
|
|
// RUN: cd %t
|
|
// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header -Werror=uninitialized folly-conv.h
|
|
// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header -Werror=uninitialized thrift_cpp2_base.h
|
|
// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header -Werror=uninitialized -fmodule-file=folly-conv.pcm -fmodule-file=thrift_cpp2_base.pcm logger_base.h
|
|
|
|
//--- Conv.h
|
|
#pragma once
|
|
|
|
template <typename _Tp, typename _Up = _Tp&&>
|
|
_Up __declval(int);
|
|
|
|
template <typename _Tp>
|
|
auto declval() noexcept -> decltype(__declval<_Tp>(0));
|
|
|
|
namespace folly {
|
|
|
|
template <class Value, class Error>
|
|
struct Expected {
|
|
template <class Yes>
|
|
auto thenOrThrow() -> decltype(declval<Value&>()) {
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
struct ExpectedHelper {
|
|
template <class Error, class T>
|
|
static constexpr Expected<T, Error> return_(T) {
|
|
return Expected<T, Error>();
|
|
}
|
|
|
|
template <class This, class Fn, class E = int, class T = ExpectedHelper>
|
|
static auto then_(This&&, Fn&&)
|
|
-> decltype(T::template return_<E>((declval<Fn>()(true), 0))) {
|
|
return Expected<int, int>();
|
|
}
|
|
};
|
|
|
|
template <class Tgt>
|
|
inline Expected<Tgt, const char*> tryTo() {
|
|
Tgt result = 0;
|
|
// In build with asserts:
|
|
// clang/lib/Sema/SemaTemplateInstantiate.cpp: llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *clang::LocalInstantiationScope::findInstantiationOf(const Decl *): Assertion `isa<LabelDecl>(D) && "declaration not instantiated in this scope"' failed.
|
|
// In release build compilation error on the line below inside lambda:
|
|
// error: variable 'result' is uninitialized when used here [-Werror,-Wuninitialized]
|
|
ExpectedHelper::then_(Expected<bool, int>(), [&](bool) { return result; });
|
|
return {};
|
|
}
|
|
|
|
} // namespace folly
|
|
|
|
inline void bar() {
|
|
folly::tryTo<int>();
|
|
}
|
|
// expected-no-diagnostics
|
|
|
|
//--- folly-conv.h
|
|
#pragma once
|
|
#include "Conv.h"
|
|
// expected-no-diagnostics
|
|
|
|
//--- thrift_cpp2_base.h
|
|
#pragma once
|
|
#include "Conv.h"
|
|
// expected-no-diagnostics
|
|
|
|
//--- logger_base.h
|
|
#pragma once
|
|
import "folly-conv.h";
|
|
import "thrift_cpp2_base.h";
|
|
|
|
inline void foo() {
|
|
folly::tryTo<unsigned>();
|
|
}
|
|
// expected-no-diagnostics
|